Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Run this script using iex (Invoke-Expression) to configure apps, create profile-extensions, install modules and other custom setup
########################################
#
# BeginSystemConfig.ps1
# iex ((New-Object System.Net.WebClient).DownloadString('https://bit.ly/2R7znLX'));
#
# Author: roysubs@hotmail.com
#
# 2019-11-25 Initial setup
# 2020-10-19 Latest Version
#
########################################
#
# To install this project:
# iex ((New-Object System.Net.WebClient).DownloadString('https://bit.ly/2R7znLX'))
# The bit.ly url above points at:
# https://gist.github.com/roysubs/61ef677591f22927afadc9ef2b657cd9/raw
# Can directly edit the project at:
# https://gist.github.com/roysubs/61ef677591f22927afadc9ef2b657cd9/edit
# Quick download of BeginSystemConfig.ps1 + ProfileExtensions.ps1 + Custom-Tools.psm1
# https://shorturl.at/cvxAC : https://gist.githubusercontent.com/roysubs/1a5eef75a70065f8f2979ccf2703f322/raw/4d02c6237732e39be069fec7aa14a367b6258f19/Get-ExtensionsAndCustomTools.ps1
# iex ((New-Object System.Net.WebClient).DownloadString('shorturl.at/cvxAC'))
# Main script goals: must remain always compatible with PowerShell v2 (for Win 7 / Win 2012 etc).
# Note: For testing on a system with PS v3+, can mimic v2 by opening a console with: powershell.exe -version 2
# Should do no *changes* to system in this, except updating PS to latest version + Chocolatey/Boxstarter Package Manager
#
# Key sections:
# Create as function as required in two locations depending on whether the console needs to be elevated or not.
function Write-Header {
Write-Host ""
Write-Host "========================================" -ForegroundColor Green
Write-Host 'Auto-Configure System' -ForegroundColor Green
Write-Host 'Add profile extensions, update PowerShell, Chocolatey, Boxstarter, and Modules' -ForegroundColor Green
Write-Host ""
Write-Host '1. Add single line to $Profile to handle profile extensions and Set-ExecutionPolicy RemoteSigned' -ForegroundColor Yellow
Write-Host ' This way the profile is minimally affected and changes can be rolled back simply by deleting' -Foreground Yellow
Write-Host ' the single profile extensions handler line from $Profile (notepad $Profile and delete the line)' -ForegroundColor Yellow
Write-Host '2. Apply latest PowerShell to system (also add .NET 4.0 / 4.5 required by various tools)' -ForegroundColor Yellow
Write-Host ' Install or update to latest Chocolatey and Boxstarter.' -ForegroundColor Yellow
Write-Host '3. Configure selected Modules to User Modules folder.' -ForegroundColor Yellow
Write-Host '4. Configure selected useful scripts and tools (no installs). ' -ForegroundColor Yellow
Write-Host '5. (ToDo): Additional tools, Boxstarter configuration etc.' -ForegroundColor Yellow
Write-Host "========================================" -ForegroundColor Green
Write-Host ""
}
####################
#
# To ready the project:
# iwr 'https://bit.ly/2R7znLX' | select -expand content | more # View BeginSystemConfig.ps1 on PS v3+
# Invoke-WebRequest (iwr) is not available on PowerShell v2. v2 compatible syntax is:
# (New-Object System.Net.WebClient).DownloadString('https://bit.ly/2R7znLX')
#
# GitHub access uses TLS. Sometimes (not always) have to specify this:
# [Net.ServicePointManager]::SecurityProtocol # To view current settings
# [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # Set type to TLS 1.2
# # Note that the above Tls12 is incompatible with Windows 7 which only has SSL3 and TLS as options.
# [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls # Set type to TLS
#
# GitHub pages are cached at the endpoint so after uploading to GitHub, the new data will not be immediately
# available and can take from ~10s to 180s to update. Clearing the DNS Client Cache might help:
# On PowerShell v2, use: [System.Net.ServicePointManager]::DnsRefreshTimeout = 0;
# On newer versions of PowerShell, can use: Clear-DnsClientCached
#
# Note the caching issues above. Some people sugget using a -Headers switch with iwr to try and get
# around that, but it has now worked reliably for me:
# - i.e. add -Headers @{"Cache-Control"="no-cache"} to iwr.
# iwr "https://gist.github.com/roysubs/61ef677591f22927afadc9ef2b657cd9/raw" -Headers @{"Cache-Control"="no-cache"} | select -Expand Content
# - Adding the iwr parameter "-DisableKeepAlive" is suggested as a fix to clear the caching, though I have not tested.
# - Preventing DNS caching seems to be a reliable fix so that iex / iwr are not using cached data. One of these should be run before iex / iwr
# to ensure that the version that iex / iwr pull is the latest.
# [System.Net.ServicePointManager]::DnsRefreshTimeout = 0
# Clear-DnsClientCache
# https://stackoverflow.com/questions/18556456/invoke-webrequest-working-after-server-is-unreachable
#
# Note that I have seen the following error on Windows 7 / 10:
# Exception calling "DownloadString" with "1" argument(s): "The request was aborted: Could not create SSL/TLS secure channel."
# Specifically fixing the security protocol to TLS in that session fixed this in some cases:
# [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls # (or optionally Tls12 on Windows 10)
# [Net.ServicePointManager]::SecurityProtocol # This will now show Tls instead of "Ssl3, Tls" and then can download
#
####################
####################
#
# Building this used the Posh-Gist Module which allows me to create and update source files (see Modules section below).
# I created a local folder and kept the project files in there and can either manually create the Gists online, or can
# use Posh-Gist with New-Gist. Created Gist-Push.ps1 and Gist-Pull.ps scripts to sync up project.
#
# # Gist-Push.ps1
# $cred = Get-Credential
# Update-Gist -Credential $cred -Id 61ef677591f22927afadc9ef2b657cd9 -Update .\BeginSystemConfig.ps1
# Update-Gist -Credential $cred -Id c37470c98c56214f09f0740fcb21ec4f -Update .\ProfileExtensions.ps1
# Update-Gist -Credential $cred -Id 5c6a16ea0964cf6d8c1f9eed7103aec8 -Update .\Custom-Tools.psm1
# Clear-DnsClientCache
#
# # Gist-Pull.ps1 (PowerShell v2 compatible
# $now = Get-Date -Format "yyyy-MM-dd__HH-mm-ss" # Adding datetime as pull is *dangerous*, can overwrite current work!
# (New-Object System.Net.WebClient).DownloadString('https://gist.github.com/roysubs/61ef677591f22927afadc9ef2b657cd9/raw') | Out-File .\BeginSystemConfig_$now.ps1
# (New-Object System.Net.WebClient).DownloadString('https://gist.github.com/roysubs/c37470c98c56214f09f0740fcb21ec4f/raw') | Out-File .\ProfileExtensions_$now.ps1
# (New-Object System.Net.WebClient).DownloadString('https://gist.github.com/roysubs/5c6a16ea0964cf6d8c1f9eed7103aec8/raw') | Out-File .\Custom-Tools_$now.psm1
# # # Variant if using PowerShell 3+ which has Invoke-WebRequest (iwr):
# # iwr 'https://gist.github.com/roysubs/61ef677591f22927afadc9ef2b657cd9/raw' | select -expand content | Out-File .\temp1.txt
# # iwr 'https://gist.github.com/roysubs/c37470c98c56214f09f0740fcb21ec4f/raw' | select -expand content | Out-File .\temp2.txt
# # iwr 'https://gist.github.com/roysubs/5c6a16ea0964cf6d8c1f9eed7103aec8/raw' | select -expand content | Out-File .\temp3.txt
# # # -Headers @{"Cache-Control"="no-cache"} does not seem to help for caching issues
#
####################
####################
#
# Problems with PowerShell v2
#
# Invoke-WebRequest (iwr) does not exist on PS v2, but Invoke-Expression (iex) does
# System.Net.WebClient is the easiest way to do it for simple GET request but if you need to do a POST request
# for a form then you will need to use System.Net.HttpWebRequest. We only need WebClient here.
# Alternatively, could download curl or wget or replicate in PowerShell. Good discussion of methods at end of this:
# https://social.technet.microsoft.com/Forums/ie/en-US/55c7e306-cab5-4bdb-9825-1909d41fa2ca/simple-curl-in-powershell-20?forum=winserverpowershell
# (New-Object System.Net.WebClient).DownloadString('https://bit.ly/2R7znLX')
# or ...
# $url = "https://gist.githubusercontent.com/roysubs/61ef677591f22927afadc9ef2b657cd9/raw"
# $output = "C:\0\BeginSystemConfig.txt"
# # $start_time = Get-Date
# $wc = New-Object System.Net.WebClient
# $wc.DownloadFile($url, $output) # or ... (New-Object System.Net.WebClient).DownloadFile($url, $output)
# # Write-Output "Time taken: $((Get-Date).Subtract($start_time).Seconds) second(s)"# Often saw the following error on Gist addresses:
# Another issue even for more modern systems is that Invoke-WebRequest does not work if IE is not 'initialized'(!).
# So when writing pipeline or DSC scripts you have to do some extra legwork first or you can use Invoke-RestMethod instead.
# On using BITS:
# Import-Module BitsTransfer (not required on PS v5 setups)
# Start-BitsTransfer -Source <url> -Destination <file-to-create-on-disk>
#
# https://social.technet.microsoft.com/Forums/en-US/eda9171e-879f-473b-bb48-687ad87fedd7/using-serviceui-powershell-and-bits-exception-from-hresult-0x800704dd?forum=ConfigMgrAppManagement
# https://social.technet.microsoft.com/Forums/ie/en-US/f006636c-27b1-46fc-8e7e-0530269c380c/startbitstransfer-powershell-remote-action-errors?forum=winserverpowershell
# https://4sysops.com/archives/use-powershell-to-download-a-file-with-http-https-and-ftp/
# https://www.thewindowsclub.com/download-file-using-windows-powershell
# https://blog.jourdant.me/post/3-ways-to-download-files-with-powershell
# https://superuser.com/questions/362152/native-alternative-to-wget-in-windows-powershell
# https://gallery.technet.microsoft.com/scriptcenter/Downloading-Files-from-dcaaf44c
# https://stackoverflow.com/questions/7715695/http-requests-with-powershell
# https://stackoverflow.com/questions/4988286/what-difference-is-there-between-webclient-and-httpwebrequest-classes-in-net
# https://stackoverflow.com/questions/7715695/http-requests-with-powershell/20941552#20941552
#
# 'Pause' is not available on PowerShell v2 so can use the below, or can just use Confirm-Choice function below (easier)
# Write-Host "Press Enter to continue...:" ; cmd /c pause | out-null
#
# "Install-Module" does not exist in PowerShell v2
#
# Get-CimInstance fails on PowerShell v2. Need to fix this (todo) for PS v2 for the Adminstrator check.
# Replace: ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber)
# With: (Get-WmiObject Win32_OperatingSystem).BuildNumber)
# Or: remove that check altogether, it is for
#
####################
####################
####################
####################
####################
#
# Header Block
#
# param() must be the first line in a script if used.
# Confirm-Choice and Elevation should be right after that.
#
####################
# param([string]$runsilently)
# $runsilenty = $false
#
# Have attempted ways to call a script online *with* parameters, but have not found a solution yet.
# Simple workaround, just put a Confirm-Choice at start to ask if want to run attended or not. Works well.
# Other options:
# - Create unnamed scriptblock from the url when invoke with call operator &), have not got this working:
# & $([scriptblock]::Create((New-Object Net.WebClient).DownloadString(https://bit.ly/2R7znLX))) silent
# https://stackoverflow.com/questions/40958417/run-powershell-script-from-url-passing-parameters
# - Download all files to temp folder then use parameters, but again cannot just kick-off from iex, so is
# also not ideal. Downloading and running would work something like:
# (New-Object System.Net.WebClient).DownloadString('https://bit.ly/2R7znLX') | Out-File "$($env:TEMP)\BeginSystemConfig.ps1" ; "$($env:TEMP)\BeingSystemConfig.ps1" <param1> <param2> ...
# https://stackoverflow.com/questions/33205298/how-to-use-powershell-to-download-a-script-file-then-execute-it-with-passing-in
####################
#
# Note optional settings to force strict limits on script execution:
# Set-StrictMode -Version Latest
# $ErrorActionPreference = 'Stop'
#
####################
####################
#
# Note on Exit / Return / Throw:
# Exit will normally exit the script and the console when in main body of a script. The only ways to get the script to exit
# but not exit the console are to put an exit inside a function, or to use a throw statement to terminate script processing.
# Using 'exit' in a function is cleaner overall (but note that in a function library called by another script, then 'return'
# should be used). BUT(!), have now discovered that the above is true for running locally, where using 'exit' is best, but
# when calling via iex, the 'exit' will kill the console(!), so falling back to using 'throw'.
# https://stackoverflow.com/questions/2022326/terminating-a-script-in-powershell
# Alao note the global solution here (solution 3). https://blog.danskingdom.com/keep-powershell-console-window-open-after-script-finishes-running/
#
####################
# Variables, create HomeFix in case of network shares (as always want to use C:\ drive, so get the name (Leaf) from $HOME)
# Need paths that fix the "Edwin issue" i.e. UsernName has changed from the path that in $env:USERPROFILE
# And also to fix the issue with VPN network paths, so check for "\\" in the profile, # $UserNameFriendly = $env:UserName
$HomeFix = $HOME
$HomeLeaf = split-path $HOME -leaf # Just get the correct username in spite of any changes to username!
$WinId = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name # This returns Hostname\WindowsIdentity, where WindowsIdentity can be different from UserProfile folder name
if ($HomeFix -like "\\*") { $HomeFix = "C:\Users\$(Split-Path $HOME -Leaf)" }
$UserModulePath = "$HomeFix\Documents\WindowsPowerShell\Modules" # $UserModulePath = "C:\Users\$HomeLeaf\Documents\WindowsPowerShell\Modules"
$UserScriptsPath = "$HomeFix\Documents\WindowsPowerShell\Scripts"
$AdminModulesPath = "C:\Program Files\WindowsPowerShell\Modules"
# The default Modules and Scripts paths are not created by default in Windows
if (!(Test-Path $UserModulePath)) { md $UserModulePath -Force -EA silent | Out-Null }
if (!(Test-Path $UserScriptsPath)) { md $UserScriptsPath -Force -EA silent | Out-Null }
$OSver = (Get-WMIObject win32_operatingsystem).Name
$PSver = $PSVersionTable.PSVersion.Major
# Get-Content -Path $profile | Select-String -Pattern "^function.+" | ForEach-Object {
# [Regex]::Matches($_, "^function ([a-z.-]+)","IgnoreCase").Groups[1].Value
# } | Where-Object { $_ -ine "prompt" } | Sort-Object
# https://codingbee.net/powershell/powershell-make-a-permanent-change-to-the-path-environment-variable
# https://www.computerperformance.co.uk/powershell/env-path/
function AddTo-Path {
param (
[string]$PathToAdd,
[Parameter(Mandatory=$true)][ValidateSet('System','User')] [string]$UserType,
[Parameter(Mandatory=$true)][ValidateSet('Path','PSModulePath')][string]$PathType
)
# AddTo-Path "C:\XXX" 'System' "PSModulePath"
if ($UserType -eq "User" ) { $RegPropertyLocation = 'HKCU:\Environment' } # also note: Registry::HKEY_LOCAL_MACHINE\ format
if ($UserType -eq "System" ) { $RegPropertyLocation = 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment' }
"`nAdd '$PathToAdd' (if not already present) into the $UserType `$$PathType"
"The '$UserType' environment variables are held in the registry at '$RegPropertyLocation'"
try { $PathOld = (Get-ItemProperty -Path $RegPropertyLocation -Name $PathType -EA silent).$PathType } catch { "ItemProperty is missing" }
"`n$UserType `$$PathType Before:`n$PathOld`n"
$PathOld = $PathOld -replace "^;", "" -replace ";$", "" # After displaying actual old path, remove leading/trailing ";" (also .trimstart / .trimend)
$PathArray = $PathOld -split ";" -replace "\\+$", "" # Create the array, removing network locations???
if ($PathArray -notcontains $PathToAdd) {
"$UserType $PathType Now:" # ; sleep -Milliseconds 100 # Might need pause to prevent text being after Path output(!)
$PathNew = "$PathOld;$PathToAdd"
Set-ItemProperty -Path $RegPropertyLocation -Name $PathType -Value $PathNew
Get-ItemProperty -Path $RegPropertyLocation -Name $PathType | select -ExpandProperty $PathType
if ($PathType -eq "Path") { $env:Path += ";$PathToAdd" } # Add to Path also for this current session
if ($PathType -eq "PSModulePath") { $env:PSModulePath += ";$PathToAdd" } # Add to PSModulePath also for this current session
"`n$PathToAdd has been added to the $UserType $PathType"
}
else {
"'$PathToAdd' is already in the $UserType $PathType. Nothing to do."
}
}
# Update PSModulePath (User), check/add the default Modules location $UserModulePath to the user PSModulePath
AddTo-Path $UserModulePath "User" "PSModulePath" | Out-Null # Add the correct Used Modules path to PSModulePath
# Update Path (User), check/add the default Scripts location $UserScriptsPath to the user Path
AddTo-Path $UserScriptsPath "User" "Path" | Out-Null # Add the correct Used Scripts path to Path
function ThrowScriptErrorAndStop {
""
throw "This is not an error. Using the 'throw' command here to halt script execution`nas 'return' / 'exit' have issues when run with Invoke-Expression from a URL ..."
}
function Confirm-Choice {
param ( [string]$Message )
$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Yes";
$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "No";
$choices = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no);
$caption = "" # Did not need this before, but now getting odd errors without it.
$answer = $host.ui.PromptForChoice($caption, $message, $choices, 0) # Set to 0 to default to "yes" and 1 to default to "no"
switch ($answer) {
0 {return 'yes'; break} # 0 is position 0, i.e. "yes"
1 {return 'no'; break} # 1 is position 1, i.e. "no"
}
}
$unattended = $false # default condition is to ask user for input
$confirm = 'Do you want to continue?' # Apart from unattended question, this is used for all other $Message values in Confirm-Choice.
if ((Get-ExecutionPolicy -Scope LocalMachine) -eq "Restricted") {
"Get-ExecutionPolicy -Scope LocalMachine => Restricted"
"It is highly recommended that you open an Administrator console and open the ExecutionPolicy."
"Set-ExecutionPolicy RemoteSigned # or 'Unrestricted' is also ok."
"This is required to run `$profile and additional scripts in this configuration."
""
if ($(Confirm-Choice "Stop this configuration run until the ExecutionPolicy is configured?`nSelect 'n' to continue anyway (expect errors).") -eq "no") { $unattended = $true }
}
# https://stackoverflow.com/questions/1059663/is-there-a-way-to-wordwrap-results-of-a-powershell-cmdlet#1059686
# Originally named Write-Wrap
function Write-Wrap {
<#
.SYNOPSIS
Wraps a string or an array of strings at the console width so that no word is broken at line enddings and nearly folds to multiple lines
.PARAMETER chunk
A string or an array of strings
.EXAMPLE
Write-Wrap -chunk $string
.EXAMPLE
$string | Write-Wrap
#>
[CmdletBinding()]Param(
[parameter(Mandatory=1, ValueFromPipeline=1, ValueFromPipelineByPropertyName=1)] [Object[]]$chunk
)
PROCESS {
$Lines = @()
foreach ($line in $chunk) {
$str = ''
$counter = 0
$line -split '\s+' | % {
$counter += $_.Length + 1
if ($counter -gt $Host.UI.RawUI.BufferSize.Width) {
$Lines += ,$str.trim()
$str = ''
$counter = $_.Length + 1
}
$str = "$str$_ "
}
$Lines += ,$str.trim()
}
$Lines
}
}
####################
#
# Note on using Write-Host: Write-Host is considered bad, or at least, it does not play nicely with the pipeline. Write-Host
# ignores the pipeline and just fires things onto the screen. This is bad from a pipeline perspective as the pipeline does
# things in a different fashion waiting for the pipeline to close etc. So, using Write-Host means that you will see some
# outputs muddled up. i.e. Outputs from the pipeline can happen after a Write-Host that comes later on in a scrip.
# There are good workarounds (mainly to make a function like Write-Host that is pipeline compliant), and they might be
# worth using, but for now I'm just sticking with Write-Host and using the "pipeline cludge" which is to put " | Out-Host"
# on the end of a few pipeline commands that might get jumbled up in ordering to make them adhere to Out-Host sequential
# ordering. i.e. With " | Out-Host" on the end of a Pipeline Cmdlet line, then the output will be ordered sequentially
# along with Write-Host commands.
# Alternatives (pipeline compliant colour outputting Cmdlets):
# https://stackoverflow.com/questions/59220186/usage-of-write-output-is-very-unreliable-compared-to-write-host/59228534#59228534
# https://stackoverflow.com/questions/2688547/multiple-foreground-colors-in-powershell-in-one-command/46046113#46046113
# https://jdhitsolutions.com/blog/powershell/3462/friday-fun-out-conditionalcolor/
# https://www.powershellgallery.com/packages?q=write-coloredoutput
#
# The best approach might be to bypass Write-Host with a custom function (functions take priority over Cmdlets as
# shown here): https://stackoverflow.com/questions/33747257/can-i-override-a-powershell-native-cmdlet-but-call-it-from-my-override
#
####################
""
# Test the path that this script is running from. If this is $null, then it was stated by iex from a URL.
$BeginPath = $MyInvocation.MyCommand.Path # echo $BeginPath
$UrlConfig = 'https://gist.github.com/roysubs/61ef677591f22927afadc9ef2b657cd9/raw'
$UrlProfileExtensions = 'https://gist.github.com/roysubs/c37470c98c56214f09f0740fcb21ec4f/raw'
$UrlCustomTools = 'https://gist.github.com/roysubs/5c6a16ea0964cf6d8c1f9eed7103aec8/raw'
if ($BeginPath -eq $null) {
"Scripts are being downloaded and installed from internet (github)."
# This is case when $BeginPath is null, i.e. the script was run via the web, so have to download all scripts.
# (New-Object System.Net.WebClient).DownloadString('https://bit.ly/2R7znLX') | Out-File "$env:TEMP\BeginSystemConfig.ps1"
(New-Object System.Net.WebClient).DownloadString($UrlConfig) | Out-File "$env:TEMP\BeginSystemConfig.ps1" -Force
(New-Object System.Net.WebClient).DownloadString($UrlProfileExtensions) | Out-File "$env:TEMP\ProfileExtensions.ps1" -Force
(New-Object System.Net.WebClient).DownloadString($UrlCustomTools) | Out-File "$env:TEMP\Custom-Tools.psm1" -Force
$BeginPath = "$env:TEMP\BeginSystemConfig.ps1"
$ScriptSetupPath = Split-Path $BeginPath # Copy the files to TEMP as staging area for local or downloaded files
}
else {
"Scripts are being installed locally from: $BeginPath"
# If the path is not null, then the script was run from the filesystem, so the scripts should be here.
# First, test if all scripts are here, if they are not, then no point in continuing.
$ScriptSetupPath = Split-Path $BeginPath # Copy the files to TEMP as staging area for local or downloaded files
if ((Test-Path "$ScriptSetupPath\BeginSystemConfig.ps1") -and (Test-Path "$ScriptSetupPath\ProfileExtensions.ps1") -and (Test-Path "$ScriptSetupPath\Custom-Tools.psm1")) {
# If the running scripts are not in TEMP, then copy them there and overwrite
# Slight issue! When elevating to admin, the called scripts are in TEMP(!), so skip copying as will be to same location!
if (Test-Path "$ScriptSetupPath\BeginSystemConfig.ps1") { Copy-Item "$ScriptSetupPath\BeginSystemConfig.ps1" "$env:TEMP\BeginSystemConfig.ps1" -Force }
if (Test-Path "$ScriptSetupPath\ProfileExtensions.ps1") { Copy-Item "$ScriptSetupPath\ProfileExtensions.ps1" "$env:TEMP\ProfileExtensions.ps1" -Force }
if (Test-Path "$ScriptSetupPath\Custom-Tools.psm1") { Copy-Item "$ScriptSetupPath\Custom-Tools.psm1" "$env:TEMP\Custom-Tools.psm1" -Force }
}
else {
"Scripts not found."
" - BeginSystemConfig.ps1"
" - ProfileExtensions.ps1"
" - Custom-Tools.psm1"
"Make sure that all required scripts are available."
"Exiting script..."
pause
exit
}
}
####################
#
# Self-elevate script if required.
#
####################
####################
#
# With the new setup, use the users Module folder instead of C:\ProgramData\WindowsPowerShell\Modules
# \\ad.ing.net\WPS\NL\P\UD\200024\YA64UG\Home\My Documents\WindowsPowerShell\Modules
# Redirect to => C:\Users\YA64UG\Documents\WindowsPowerShell\Modules
# Profile loading times are *extremely* slow when running on an ING EndPoint connection.
# Laptop without EndPoint => PowerShell loads in <1 sec
# Laptop with EndPoint => PowerShell loads in 6.5 to 9 sec
# Laptop with EndPoint => PowerShell loads in 3.5 sec (if move rofile extensions to C:\ locally)
# Laptop with EndPoint => PowerShell loads in 1 sec (if also load C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1)
# Plan is then:
# a) Try to move to profile.ps1 under System32 (will only work if TestAdministrator -eq True)
# b) Use default user folder if cannot do that. i.e. \\ad.ing.net\WPS\NL\P\UD\200024\YA64UG\Home\My Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
# But this is not a problem because b) is only a problem if on a laptop with VPN because if on a server etc, the default user folder is fine!
# So, if on my laptop:
# Profile = [C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1] + [C:\User\YA64UG\WindowsPowerShell\profile.ps1]
# if(Test-File $Profile) { rename $Profile $Profile-disabled }
# Modules = [C:\Users\YA64UG\Documents\WindowsPowerShell\Modules]
# Check that this is in $env:PSModulePath
# Delete the H: version of Modules completely!
#
# 1. if(Test-Administrator -eq True) => ask if want Admin install C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1 or Default User install
# Suggest to only do an Admin install on personal laptop
# 2.
# 3. Scripts => C:\Users\YA64UG\Documents\WindowsPowerShell\Scripts
# Make sure that this is on the path
# mklinks => C:\PS\ (so profile will be at C:\PS\profile.ps1), C:\PS\Scripts, C:\PS\Modules
# No, can't do this as might not be admin!
# Just leave at normal locations but have go definitions to jump to these locations.
#
# # To get around the ING problem with working from home.
# By mirroring the normal profile location, then everything else will work normally.
# Profile extensions will be placed in the local profile location rather than the ING one.
# "Microsoft.PowerShell_profile.ps1_extensions.ps1"
# By only appyling this to systems from ING, everything else will work normally.
# So the below is what should be pushed to C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1
#
# # # # if ($env:USERNAME -eq "ya64ug") {
# if ($(get-wmiobject -class Win32_ComputerSystem).PrimaryOwnerName -eq "ING")
# $PROFILE.CurrentUserAllHosts = "$HomeFix\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1"
# $PROFILE.CurrentUserCurrentHost = "$HomeFix\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1"
# . $PROFILE
# cd $HomeFix
# }
#
# $PROFILE = "$HomeFix\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1"
#
# Note that the above breaks the $PROFILE definition!
# ($PROFILE).GetType()
# IsPublic IsSerial Name BaseType
# -------- -------- ---- --------
# True True String System.Object
#
# $PROFILE | Format-List * -Force
# AllUsersAllHosts : C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1
# AllUsersCurrentHost : C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1
# CurrentUserAllHosts : \\ad.ing.net\WPS\NL\P\UD\200024\YA64UG\Home\My Documents\WindowsPowerShell\profile.ps1
# CurrentUserCurrentHost : \\ad.ing.net\WPS\NL\P\UD\200024\YA64UG\Home\My Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
# Length : 107
#
# But, after the change above, making $PROFILE a single string:
# $PROFILE | Format-List * -Force
# C:\Users\ya64ug\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
#
# So instead, just change those parts:
# $PROFILE.CurrentUserAllHosts = "$HomeFix\Documents\WindowsPowerShell\profile.ps1"
# $PROFILE.CurrentUserCurrentHost = "$HomeFix\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1"
#
# More here: https://devblogs.microsoft.com/scripting/understanding-the-six-powershell-profiles/
#
####################
# Define general Test-Administrator here and the full code to auto-elevate the script to run as Admin
# Moving away from auto-elevation so can manage everything in User folders (so scripts can run on any system)
function Test-Administrator {
$user = [Security.Principal.WindowsIdentity]::GetCurrent();
(New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
# if ( ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) -ne $True) {
# Write-Header
# Write-Host "Script can only continue if user is Elevated / Administrator ..." -ForegroundColor White -BackgroundColor Red
# Write-Host "Current console is not Elevated so will attempt to auto-elevate." -ForegroundColor Red -BackgroundColor White
# Write-Host ""
# # if (!(Test-Path("$($env:TEMP)\ElevatedScriptStop.txt"))) { New-Item -ItemType File "$($env:TEMP)\ElevatedScriptStop.txt" }
# if (-Not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) {
# # if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 6000) { # Avoid Get-CimInstance for PS v2 compatibility
# # if (((Get-WmiObject Win32_OperatingSystem).BuildNumber) -ge 6000) {
#
# # If the script was run locally, just copy that (and all other scripts) to $env:TEMP and use them.
# # If the script was run from a url, then download latest copies from there.
# if ($BeginPath -eq $null) {
# (New-Object System.Net.WebClient).DownloadString('https://bit.ly/2R7znLX') | Out-File "$($env:TEMP)\BeginSystemConfig.ps1"
# }
# # Scripts were copied to TEMP earlier, will only be changed if was downloaded from Gist
# $CommandLine = "-File `"" + "$($env:TEMP)\BeginSystemConfig.ps1" + "`""
# if (Test-Path (Join-Path -Path "$($PSHOME)" -ChildPath "powershell.exe")) { $HostBinary = Join-Path -Path "$($PSHOME)" -ChildPath "powershell.exe" } # PowerShell 5.1
# if (Test-Path (Join-Path -Path "$($PSHOME)" -ChildPath "pwsh.exe")) { $HostBinary = Join-Path -Path "$($PSHOME)" -ChildPath "pwsh.exe" } # PowerShell Core 6 / 7
# try { Start-Process -FilePath $HostBinary -Verb Runas -ArgumentList $CommandLine -EA silent }
# catch { echo "wtf" }
# "Stopping script here using a 'throw' statement as exit/return can close window"
# "when script is called from internet URL."
# ""
# ThrowScriptErrorAndStop
# }
# }
# An argument for downloading and then running scripts is that we can then self-elevate the script.
# This is not possible if calling the script from a URL via Invoke-Expression, so for this situation
# modify the block to download the script and then call the script.
# Add this snippet at the beginning of any script that requires elevation to run properly. It works by
# starting a new elevated PowerShell window and then re-executes the script in this new window, if necessary.
# If User Account Control (UAC) is enabled, you will get a UAC prompt. If the script is already running in an
# elevated PowerShell session or UAC is disabled, the script will run normally. This code also allows you to
# right-click the script in File Explorer and select "Run with PowerShell".
# http://www.expta.com/2017/03/how-to-self-elevate-powershell-script.html
# This is the unmodified code block from the above. Try to use with as little modification sa possible.
# In fact, no need to modify, just prompt at start of script if want to run unattended or attended.
#
# if (-Not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) {
# if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 6000) {
# $CommandLine = "-File `"" + $MyInvocation.MyCommand.Path + "`" " + $MyInvocation.UnboundArguments
# Start-Process -FilePath PowerShell.exe -Verb Runas -ArgumentList $CommandLine
# Exit
# }
# }
# In fact, *must* modify. If elevating, the script will restart, but if called from iex, then it has nothing
# to restart! In that case, must download the scripts and run them locally.
# iwr 'https://gist.github.com/roysubs/c37470c98c56214f09f0740fcb21ec4f/raw' | select -expand content | Out-File .\temp\ProfileExtensions.ps1
# iwr 'https://gist.github.com/roysubs/5c6a16ea0964cf6d8c1f9eed7103aec8/raw' | select -expand content | Out-File .\temp\Custom-Tools.psm1
# iwr 'https://gist.github.com/roysubs/908525ae135e7d31a4fd13bd111b50e9/raw' | select -expand content | Out-File .\temp\Gist-Push.ps1
Write-Header
# Elevation will restart the script so don't ask this question until after that point
if ($(Confirm-Choice "Prompt all main action steps during setup?`nSelect 'n' to make all actions unattended.") -eq "no") { $unattended = $true }
####################
#
# Main Script
#
####################
$start_time = Get-Date # Put following lines at end of script to time it
# $hr = (Get-Date).Subtract($start_time).Hours ; $min = (Get-Date).Subtract($start_time).Minutes ; $sec = (Get-Date).Subtract($start_time).Seconds
# if ($hr -ne 0) { $times += "$hr hr " } ; if ($min -ne 0) { $times += "$min min " } ; $times += "$sec sec"
# "Script took $times to complete" # $((Get-Date).Subtract($start_time).TotalSeconds)
# Set-ExecutionPolicy RemoteSigned
Write-Host ""
Write-Host "Try to set the execution policy to RemoteSigned for this system" -ForegroundColor Yellow -BackgroundColor Black
try {
Set-ExecutionPolicy RemoteSigned -Force # Need
} catch {
Write-Host "`nWARNING: 'Set-ExecutionPolicy RemoteSigned' failed to execute." -ForegroundColor Yellow
Write-Host "This is often due to Group Policy restrictions on corporate builds.`n" -ForegroundColor Yellow
Write-Host "'Get-ExecutionPolicy -List' (show current execution policy list):`n"
Get-ExecutionPolicy -List | ft
}
# Set TLS for GitHub compatibility
try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls } catch { } # Windows 7 compatible
try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } catch { } # Windows 10 compatible
# BeingSystemConfig.ps1 to initialise everything, including chocolatey / boxstarter setup.
# Boxstarter module should be installed to get access to Install-BoxstarterPackage
Write-Host ""
Write-Host 'The object of this script is to configure basic System and PowerShell configurations.'
Write-Host 'Avoiding extensive modifications and sticking to core functionality. This control'
Write-Host 'script calls online Gists for all configuration (can also be run offline if the'
Write-Host 'internet is unavailable and the files are all in the same folder). To customise for'
Write-Host 'your own needs, the three Gists can be copied, edited, and then executed using a single'
Write-Host 'command to distribute all required settings to a new system.'
Write-Host ""
Write-Host "# The current script is used to chain together these system configurations" -ForegroundColor DarkGreen
Write-Host "iwr '$UrlConfig' | select -expandproperty content | more" -ForegroundColor Magenta
Write-Host "(New-Object System.Net.WebClient).DownloadString('$UrlConfig')"
Write-Host "# The Profile Extension script extends `$profile" -ForegroundColor DarkGreen
Write-Host "iwr '$UrlProfileExtensions' | select -expandproperty content | more" -ForegroundColor Magenta
Write-Host "(New-Object System.Net.WebClient).DownloadString('$UrlProfileExtensions')"
Write-Host "# Custom-Tools.psm1 Module to make useful generic functions available to console." -ForegroundColor DarkGreen
Write-Host "iwr '$UrlCustomTools' | select -expandproperty content | more" -ForegroundColor Magenta
Write-Host "(New-Object System.Net.WebClient).DownloadString('$UrlCustomTools')"
Write-Host ""
# Deprecated requirement to install as Admin, but leave this for reference.
# Write-Host "Script can only continue if user is Elevated / Administrator ..." -ForegroundColor White -BackgroundColor Red
# if ( ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) -ne $True) {
# Write-Host "Current console is not Elevated so script will exit. Rerun as Administrator to fix this." -ForegroundColor Red -BackgroundColor White
# Write-Host ""
# ThrowScriptErrorAndStop
# } else {
# Write-Host "Current console is Elevated so script can continue." -ForegroundColor Green
# }
Write-Host 'Please review the above URLs to check that the changes are what you expect before continuing.'
Write-Host ""
# https://stackoverflow.com/questions/12522539/github-gist-editing-without-changing-url
if ($unattended -eq $false) { if ($(Confirm-Choice $confirm) -eq "no") { ThrowScriptErrorAndStop } }
####################
#
# Setup Profile Extensions and Custom Tools Module
#
####################
Write-Host ''
Write-Host ''
Write-Host "`n========================================" -ForegroundColor Green
Write-Host ''
Write-Host '1. Configure Custom-Tools and setup Profile Extensions in $Profile.' -ForegroundColor Green
Write-Host ''
Write-Host ' Check for the profile extensions in same folder as $Profile. If not present, the' -ForegroundColor Yellow
Write-Host ' latest profile extensions will be downloaded from Gist. A single line will then be' -ForegroundColor Yellow
Write-Host ' added into $Profile to load the profile extensions from $Profile into all new consoles.' -ForegroundColor Yellow
Write-Host ''
Write-Host ' To force the latest profile extensions, either rerun this script which will overwrite it' -ForegroundColor Yellow
Write-Host ' or delete the profile extensions and it will be downloaded on opening the next console.' -ForegroundColor Yellow
Write-Host ''
Write-Host ' The reason for having the profile extensions separately is to keep the profile clean and' -ForegroundColor Yellow
Write-Host ' to keep additions synced against a known working online copy.' -ForegroundColor Yellow
if($OSver -like "*Server*") {
Write-Host ''
Write-Host " The Operating System is of type 'Server' so profile extensions will *not* load" -ForegroundColour Yello -BackgroundColor Black
Write-Host " by default in any console. Profile extensions can be loaded on demand by running" -ForegroundColour Yellow -BackgroundColor Black
Write-Host " the 'Enable-Extensions' function that will now be setup in `$profile." -ForegroundColour Yellow -BackgroundColor Black
}
Write-Host ''
Write-Host "========================================`n" -ForegroundColor Green
Write-Host ''
Write-Host ""
Write-Host "Update and reinstall Custom-Tools.psm1 Module." -ForegroundColor Yellow -BackgroundColor Black
Write-Host "The Module contains many generic functions (including some required later in this setup)."
Write-Host "View functions in the module with 'myfunctions' / 'mods' / 'mod custom-tools' or view module contents with:"
Write-Host " get-command -module custom-tools" -ForegroundColor Yellow
$CustomTools = "$HomeFix\Documents\WindowsPowerShell\Modules\Custom-Tools\Custom-Tools.psm1"
if ([bool](Get-Module Custom-Tools -ListAvailable) -eq $true) {
if ($PSver -gt 4) { Uninstall-Module Custom-Tools -EA Silent -Force -Verbose } # Uninstall-Module is only in PS v4+
else { "Need to be running PS v5 or higher to run Uninstall-Module" }
}
if (!(Test-Path (Split-Path $CustomTools))) { New-Item (Split-Path $CustomTools) -ItemType Directory -Force } # Create folder if required
if (Test-Path ($CustomTools)) { rm $CustomTools } # Delete old version of the .psm1 if it was already there
"$BeginPath is currently running script."
"$(Split-Path $BeginPath)\Custom-Tools.psm1 will be used to load Custom-Tools Module."
if ($BeginPath -eq $null) {
# Case when scripts were downloaded
# try { (New-Object System.Net.WebClient).DownloadString("$UrlCustomTools") | Out-File $CustomTools ; echo "Downloaded Custom-Tools.psm1 Module from internet ..." }
# catch { Write-Host "Failed to download! Check internet/TLS settings before retrying." -ForegroundColor Red }
Copy-Item "$ScriptSetupPath\Custom-Tools.psm1" $CustomTools -Force
}
else {
# First try to use the version in same folder as BeginSystemConfig
Copy-Item "$ScriptSetupPath\Custom-Tools.psm1" $CustomTools -Force
Write-Host "$ScriptSetupPath\Custom-Tools.psm1 was copied to Custom-Tools Path: $CustomTools"
Write-Host "Using local version of Custom-Tools.psm1 from $BeginPath ..."
}
# If still have nothing, try to download from Github
# else {
# try { (New-Object System.Net.WebClient).DownloadString("$UrlCustomTools") | Out-File $CustomTools }
# catch { Write-Host "Failed to download! Check internet/TLS settings before retrying." -ForegroundColor Red }
# }
# }
Import-Module Custom-Tools -Force -Verbose # Don't require full path, it should search for it in standard PSModulePaths
$x = ""; foreach ($i in (get-command -module Custom-Tools).Name) {$x = "$x, $i"} ; "" ; Write-Wrap $x.trimstart(", ") ; ""
# Alternative method to dotsource all scripts in a given folder along with $Profile
# $MyFunctionsDir = "$env:USERPROFILE\Documents\WindowsPowerShell\Functions"
# Get-ChildItem "$MyFunctionsDir\*.ps1" | % {.$_}
if ($unattended -eq $false) { if ($(Confirm-Choice $confirm) -eq "no") { ThrowScriptErrorAndStop } }
$ProfileFolder = $Profile | Split-Path -Parent
$ProfileFile = $Profile | Split-Path -Leaf
$ProfileExtensions = Join-Path $ProfileFolder "$($ProfileFile)_extensions.ps1"
Write-Host "Profile : $Profile"
Write-Host "ProfileExtensions : $ProfileExtensions"
Write-Host ""
Write-Host "Note that the Profile path below is determined by the currently running 'Host'."
Write-Host "i.e. The host is usually either the PowerShell Console, or PowerShell ISE, or"
Write-Host "Visual Studio Code, each of which will have their own Profile path which you can"
Write-Host "see by typing `$Profile from within that given host. Other hosts can exist, such"
Write-Host "as PowerShell Core running under Linux, but the above are the most usual on Wwindows."
Write-Host ""
Write-Host "You can see more information on the current host by typing '`$host' at the console:"
$host # Will show the current host
# Create $Profile folder and file if they do not exist
if (!(Test-Path $ProfileFolder)) { New-Item -Type Directory $ProfileFolder -Force }
if (!(Test-Path $Profile)) { New-Item -Type File $Profile -Force }
# Create a backup of the extensions in case user has modified this directly.
if (Test-Path ($ProfileExtensions)) {
Write-Host "`nCreating backup of existing profile extensions in case download fails ..."
Move-Item -Path "$($ProfileExtensions)" -Destination "$($ProfileExtensions)_$(Get-Date -format "yyyy-MM-dd__HH-mm-ss").txt"
}
Write-Host "`nGet latest profile extensions (locally if available or download) ..."
if ($BeginPath -eq $null) { # If BeginPath is null, files should still be in env:TEMP from previous download
# try { (New-Object System.Net.WebClient).DownloadString("$UrlProfileExtensions") | Out-File $ProfileExtensions -Force ; echo "Downloaded profile extensions from internet ..." }
# catch { Write-Host "Failed to download! Check internet/TLS settings before retrying." -ForegroundColor Red }
Copy-Item "$ScriptSetupPath\ProfileExtensions.ps1" $ProfileExtensions -Force
} else {
# First try to use the version in same folder as BeginSystemConfig
Copy-Item "$ScriptSetupPath\ProfileExtensions.ps1" $ProfileExtensions -Force
Write-Host "$ScriptSetupPath\ProfileExtensions.ps1 was copied to $ProfileExtensions"
Write-Host "Using local version of Profile Extensions from $BeginPath ..."
}
# # If still have nothing, try to download from Github
# else {
# try { (New-Object System.Net.WebClient).DownloadString("$UrlProfileExtensions") | Out-File $UrlProfileExtensions }
# catch { Write-Host "Failed to download! Check internet/TLS settings before retrying." -ForegroundColor Red }
# }
# If the script was run from a url, then download latest copies from there.
# For working offline, use the latest profile extensions locally if available by copying to TEMP and using same logic.
# if ($null -eq $BeginPath ) {
# if (Test-Path "$($env:TEMP)\ProfileExtensions.ps1") {
# Copy-Item "$($env:TEMP)\ProfileExtensions.ps1" $ProfileExtensions -Force
# echo "`nUsing local profile extensions from $($env:TEMP)\ProfileExtensions.ps1..."
# } else {
# # If $ProfileExtensions was not there, just get from online
# try { (New-Object System.Net.WebClient).DownloadString("$UrlProfileExtensions") | Out-File $ProfileExtensions ; echo "Downloaded profile extensions from internet ..."}
# catch { Write-Host "Failed to download! Check internet/TLS settings before retrying." -ForegroundColor Red }
# }
# }
if (Test-Path ($ProfileExtensions)) {
Write-Host "`nDotsourcing new profile extensions into this current session ..."
. $ProfileExtensions
}
else {
Write-Host "`nProfile extensions could not be loaded locally (missing) or downloaded from internet."
Write-Host "If downloaded from internet, check internet/TLS settings before retrying."
pause
}
# If running on a server OS, or "Administrator" or hostname is "SJ*CAL", do not autoload extensions.
# In that case, put a function into $profile to enable: . Enable-Extensions
if ($OSver -like "*Server*") {
# Remove the Enable-Extensions line by getting the content *minus* the line to remove "-NotMatch" and adding it back into the profile
Write-Host "`nRemoving profile extensions handler line from `$Profile to ensure that it is positioned at the end of the script ..."
Set-Content -Path $profile -Value (Get-Content -Path $profile | Select-String -Pattern '^if \(\!\(Test-Path \("\$\(\$Profile\)_extensions\.ps1\"\)\)\) \{ try { \(New' -NotMatch)
Set-Content -Path $profile -Value (Get-Content -Path $profile | Select-String -Pattern '^function Enable-Extensions { if' -NotMatch)
# Set-Content -Path $profile -Value (Get-Content -Path $profile | Select-String -Pattern '^if \($MyInvocation.InvocationName -eq "Enable-Extensions"\)' -NotMatch)
# Append the Enable-Extensions line to end of $profile
Write-Host "`nAdding updated profile extensions handler line to `$Profile ...`n"
$ProfileExtensionsHandler = "function Enable-Extensions { if (Test-Path (""`$(`$Profile)_extensions.ps1"")) { " # Need separate line for the $($Profile) expansion
$ProfileExtensionsHandler += 'if ($MyInvocation.InvocationName -eq "Enable-Extensions") { "`nWarning: Must dotsource Enable-Extensions or it will not be added!`n`n. Enable-Extensions`n" } else { . "$($Profile)_extensions.ps1" -EA silent } } }'
Add-Content -Path $profile -Value $ProfileExtensionsHandler -PassThru
Write-Host ""
Write-Host "The profile extensions handler has *not* been added to `$Profile as the Operating System" -ForegroundColor Yellow -BackgroundColor Black
Write-Host "is of type 'Server'. However, the 'Enable-Extensions' function has been added to `$Profile" -ForegroundColor Yellow -BackgroundColor Black
Write-Host "and can be loaded at any time using by dotsourcing into the current session:" -ForegroundColor Yellow -BackgroundColor Black
Write-Host " . Enable-Extensions" -ForegroundColor Cyan -BackgroundColor Black
Write-Host ""
} else {
# Want to put these lines at the end of $profile, so strip the matching lines and rewrite to the same file
# Set-Content -Path "C:\myfile.txt" -Value (Get-Content -Path "C:\myfile.txt" | Select-String -Pattern 'H\|159' -NotMatch)
Write-Host "`nRemoving profile extensions handler line from `$Profile to ensure that it is positioned at the end of the script ..."
# Set-Content -Path $profile -Value (Get-Content -Path $profile | Select-String -Pattern '^\[Net\.ServicePointManager\]::SecurityProtocol' -NotMatch)
# Remove the Tls setting as not compatible with PowerShell v2
# Set-Content -Path $profile -Value (Get-Content -Path $profile | Select-String -Pattern '^\$UrlProfileExtensions \= ' -NotMatch) -EA SilentlyContinue
Set-Content -Path $profile -Value (Get-Content -Path $profile | Select-String -Pattern '^if \(\!\(Test-Path \("\$\(\$Profile\)_extensions\.ps1\"\)\)\) \{ try { \(New' -NotMatch)
# Then append the lines in the correct order to the end of $profile. Note "-PassTru" switch to display the result as well as writing to file
Write-Host "`nAdding updated profile extensions handler line to `$Profile ...`n"
# $ProfileExtensionsHandler = "`$UrlProfileExtensions = ""$UrlProfileExtensions"" ; "
$ProfileExtensionsHandler = "if (!(Test-Path (""`$(`$Profile)_extensions.ps1""))) { try { (New-Object System.Net.WebClient).DownloadString('$UrlProfileExtensions') | Out-File ""`$(`$Profile)_extensions.ps1"" } "
$ProfileExtensionsHandler += 'catch { "Could not download profile extensions, check internet/TLS before opening a new console." } } ; '
$ProfileExtensionsHandler += '. "$($Profile)_extensions.ps1" -EA silent'
Add-Content -Path $profile -Value $ProfileExtensionsHandler -PassThru
Write-Host ""
Write-Host "The profile extensions handler has been added to `$Profile and so will be" -ForegroundColor Yellow -BackgroundColor Black
Write-Host "loaded by default in all console sessions." -ForegroundColor Yellow -BackgroundColor Black
Write-Host ""
# Considerations for building strings: https://powershellexplained.com/2017-11-20-Powershell-StringBuilder/
# Alternatively, doing three Add-Content lines with -NoNewLine would also work fine.
# Added ErrorAction (EA) SilentlyContinue to suppress errors if cannot reach the URL
# Removed the Tls setting as this is not compatible with PowerShell v2
# Add-Content -Path $profile -Value "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12" -PassThru
}
Write-Host ""
if ($unattended -eq $false) { if ($(Confirm-Choice $confirm) -eq "no") { ThrowScriptErrorAndStop } }
# Update-Help to download latest help files
# Note that Update-Help does not exist in vanilla Windows 7 / PowerShell v2 (!)
# Check Last Modified Date of "$($env:TEMP)\Update-Help-Check.flag"
# Create / Touch file in $env:TEMP
# https://superuser.com/questions/251263/the-last-access-date-is-not-changed-even-after-reading-the-file-on-windows-7
####################
#
# DEPRECATE ALL OF THIS !!!
# Takes too long to complete, and anyway, 'm' in the Custom-Tools performs the same task to update all help files.
#
####################
# $updatefile = "$($env:TEMP)\ps_Update-Help-$($PSVersionTable.PSVersion.ToString()).flag"
# if (Test-Path $updatefile) { [datetime]$updatetime = (Get-Item $updatefile).LastWriteTime }
# [int]$helpolderthan = 20
# [datetime]$dateinpast = (Get-Date).AddDays(-$helpolderthan)
#
# if (Test-Administrator -eq $true) {
# Write-Host ""
# Write-Host ""
# Write-Host "n========================================" -ForegroundColor Green
# Write-Host "Update Help Files if more than $helpolderthan days old." -F Yellow -B Black
# Write-Host "Checking PowerShell Help definitions ..." -F Yellow -B Black
# Write-Host ""
# Write-Host "Note: This section will only show if you are running as Administrator."
# Write-Host "========================================" -ForegroundColor Green
# Write-Host ""
#
# if ($PSVersionTable.PSVersion.Major -eq 2) {
# Write-Host "Update-Help Cmdlet does not exist on PowerShell v2." -ForegroundColor Red
# Write-Host "Skipping help definitions update ..." -ForegroundColor Red
# } else {
#
# if (Test-Path $updatefile) {
# "Current Date minus $helpolderthan : $((Get-Date).AddDays(-$helpolderthan))" # Note that this has a "-" so this is back 20 days from the current day
# "Date on help update flag file : $updatetime"
#
# if ($updatetime -lt $dateinpast) {
# Write-Host "Help files only update if more than $($helpolderthan.ToString()) days old. A flag file is" -ForegroundColor Green
# Write-Host "kept in the user Temp folder timestamped at last update time to check this ..." -ForegroundColor Green
# Write-Host "The flag file is more than days old, so Help file definitions will update ..." -ForegroundColor Green
# (Get-ChildItem $updatefile).LastWriteTime = Get-Date # touch the flat file to today's date
# Update-Help -ErrorAction SilentlyContinue
# # Run this with -EA silent due to the various errors that always happen as a result of bad manifests, which will be similar to:
# # Note that this will not suppress all error messages! so need to add | Out-Null also
# # update-help : Failed to update Help for the module(s) 'AppvClient, ConfigDefender, Defender, HgsClient, HgsDiagnostics, HostNetworkingService,
# # Microsoft.PowerShell.ODataUtils, Microsoft.PowerShell.Operation.Validation, UEV, Whea, WindowsDeveloperLicense' with UI culture(s) {en-GB} : Unable to
# # connect to Help content. The server on which Help content is stored might not be available. Verify that the server is available, or wait until the server is
# # back online, and then try the command again.
# } else {
# Write-Wrap "The flag file used to determine if help definitions should be updated is less than $($helpolderthan.ToString()) days old so help file will not be updated ..."
# }
# } else {
# Write-Host "No help definitions checkfile found, creating a new checkfile and Update-Help will run ..."
# New-Item -ItemType File $updatefile
# Update-Help -ErrorAction SilentlyContinue
# }
# }
# Write-Host ""
# if ($unattended -eq $false) { if ($(Confirm-Choice $confirm) -eq "no") { ThrowScriptErrorAndStop } }
# }
# else {
# "Must be Administrator to update help files"
# }
####################
#
# Update PowerShell, Chocolatey, Boxstarter
#
####################
if (Test-Administrator -eq $true) {
Write-Host ''
Write-Host ''
Write-Host "`n========================================" -ForegroundColor Green
Write-Host ''
Write-Host '2. Update PowerShell, Chocolatey, Boxstarter' -ForegroundColor Green
Write-Host ''
Write-Host ' Only run the web-installers if not already found to be installed.' -ForegroundColor Yellow
Write-Host ' Check various system configurations and update if required.' -ForegroundColor Yellow
Write-Host ''
Write-Host ' Also, if on Windows 7, test for Service Pack 1, .NET 4.5 etc.' -ForegroundColor Yellow
Write-Host ''
Write-Host "========================================`n" -ForegroundColor Green
Write-Host ''
if ($unattended -eq $false) { if ($(Confirm-Choice $confirm) -eq "no") { ThrowScriptErrorAndStop } }
Write-Host ""
Write-Host "Test for profile, create if it does not currently exist" -ForegroundColor Yellow -BackgroundColor Black
Write-Host "$profile"
if(!(Test-Path (Split-Path $profile))) { New-Item -ItemType Directory (Split-Path $profile) } # Create profile folder if not already there
if(!(Test-Path $profile)) { New-Item -ItemType File $profile } # Create $profile if not already there
Write-Host ""
try {
Set-ExecutionPolicy Bypass -Scope Process -Force
} catch {
Write-Host "`nWARNING: 'Set-ExecutionPolicy Bypass -Scope Process -Force' failed to execute." -ForegroundColor Yellow
Write-Host "This is often due to Group Policy restrictions on corporate builds.`n" -ForegroundColor Yellow
Write-Host "'Get-ExecutionPolicy -List' (show current execution policy list):`n"
Get-ExecutionPolicy -List | ft
}
# The actual error in this case was:
# Set-ExecutionPolicy : Windows PowerShell updated your execution policy successfully, but the setting is overridden
# by a policy defined at a more specific scope. Due to the override, your shell will retain its current effective
# execution policy of Unrestricted. Type "Get-ExecutionPolicy -List" to view your execution policy settings. For more
# information please see "Get-Help Set-ExecutionPolicy".
# Note that the 'Unrestricted' setting is more open than the setting attempted in the above.
# However, iff you have 'Administrator' access (which is required to run this script), then it should run ok.
# To take and run the string was a little awkward. Tried:
# dotsourcing, &, Start-Job, but in the end Invoke-Expression (iex) was required!
# https://stackoverflow.com/questions/12850487/invoke-a-second-script-with-arguments-from-a-script
function IfExistSkipCommand ($toCheck, $toRun) {
if (Test-Path($toCheck)) {
Write-Host "Item exists : $toCheck" -ForegroundColor Green
Write-Host "Will skip installer: $toRun`n" -ForegroundColor Cyan
} else {
Write-Host "Item does not exist: $toCheck" -ForegroundColor Green
Write-Host "Will run installer : $toRun`n" -ForegroundColor Cyan
Invoke-Expression $toRun
}
}
# Chocolatey, assume ok if choco.exe is there
Write-Host "Test for Chocolatey, download and install if it does not currently exist" -ForegroundColor Yellow -BackgroundColor Black
IfExistSkipCommand "C:\ProgramData\chocolatey\choco.exe" "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))"
# Manually apply the chocolateyProfile
if (Test-Path("C:\ProgramData\chocolatey\helpers\chocolateyProfile.psm1")) { Import-Module "C:\ProgramData\chocolatey\helpers\chocolateyProfile.psm1" }
$env:Path += ";C:\ProgramData\chocolatey" # Add to Path just for this script session
# To update System Path: [Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\bin", "Machine")
# To update User Path: [Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path", "User") + ";C:\bin", "User")
# Boxstarter, assume ok if BoxstarterShell.ps1 is there
Write-Host ""
Write-Host "Test for Boxstarter, download and install if it does not currently exist" -ForegroundColor Yellow -BackgroundColor Black
IfExistSkipCommand "C:\ProgramData\Boxstarter\BoxstarterShell.ps1" "choco upgrade -y Boxstarter"
if (-not (Get-Module -ListAvailable 'Boxstarter.Chocolatey')) { Import-Module 'C:\ProgramData\Boxstarter\Boxstarter.Chocolatey\Boxstarter.Chocolatey.psd1' }
# Get-Module # Display *loaded* modules
# Get-Module -ListAvailable # Display *available and NOT loaded* modules
# PowerShell 5+
Write-Host "Test for PowerShell, download and install if it is not at least version 5.x" -ForegroundColor Yellow -BackgroundColor Black
if ($PSVersionTable.PSversion.Major -lt 5) { choco upgrade -y PowerShell }
# Chocolatey add-ons, best not to install anything at this stage, but leave here as a reminder
# Write-Host "Test for Chocolatey add-ons, download and install" -ForegroundColor Yellow -BackgroundColor Black
# choco upgrade -y ChocoShortcuts
# https://stackoverflow.com/questions/56728440/how-to-import-chocolatey-function-core-and-extension-to-powershell-sessions
####################
# Windows 7 checks (SP1 etc, Work in progress ...)
####################
# Remember internal versions: Windows 7 is "6.1", Win 8 is "6.2", Win 10 is "6.3" or something like that(!)
# For SP0 : (Get-WmiObject -Class Win32_OperatingSystem).Version -eq 6.1.7600
if ([Environment]::OSVersion.Version.Major -eq 6) {
Write-Host ''
Write-Host "`n========================================" -ForegroundColor DarkGreen
Write-Host ''
Write-Host 'Subsection if OS is Windows 7' -ForegroundColor DarkGreen
Write-Host ''
Write-Host '- Check for SP1, install if required.' -ForegroundColor DarkGreen
Write-Host '- Check for a .NET Framework that is at least 4.5+.' -ForegroundColor DarkGreen
Write-Host ''
Write-Host "========================================`n" -ForegroundColor DarkGreen
Write-Host ''
$colOS = Get-WmiObject -class Win32_OperatingSystem -computername '.' # $Strcomputer
foreach($objComp in $colOS) {
$OScaption = $objComp.Caption
$OSdescription = $objComp.Description
$SPversion = $objComp.ServicePackMajorVersion
}
Write-Host "Rough Windows 7 checks ... work on these on vanilla Windows 7 setup ..."
Write-Host "Win32_OperatingSystem Caption = $OScaption"
Write-Host "Win32_OperatingSystem Description = $OSdescription"
Write-Host "Win32_OperatingSystem ServicePackMajorVersion = $SPversion"
# Install Service Pack 1 for Windows 7 if not present
Write-Host ""
Write-Host "Test for Service Pack 1, install if required ..."
if ($SPversion -ne 1) { choco install kb976932 -y } # Install SP1 if on Win 7 and not installed
# Test for a version of .NET 4.x that is *at least* 4.5
# https://stackoverflow.com/questions/3487265/powershell-script-to-return-versions-of-net-framework-on-a-machine
$colVers = Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -recurse |
Get-ItemProperty -name Version,Release -EA 0 | where { $_.PSChildName -match '^(?!S)\p{L}'} |
Select Version
# Select PSChildName, Version, Release
$dotnet45required = $true
foreach($objVer in $colVers) {
$version = $objVer.Version
if ($version -match '^4\.[56789]') { $dotnet45required = $false }
}
Write-Host ""
Write-Host "Test for .NET 4.5+, install if required ..."
if ($dotnet45required -eq $true) { choco install dotnet4.5 -y } # .NET 4.5
}
}
####################
#
# Install selected Modules
#
####################
# Module duplication deployment with Azure DevOps:
# https://stackoverflow.com/questions/58686463/powershell-module-deployment-duplication
#
# https://github.com/PowerShell/PowerShellGet/issues/234 - discussions of hoops have to go through to remove old and install newest version
# try {
# # If the module is already installed, use Update, otherwise use Install
# if ([bool](Get-Module $MyModuleName -ListAvailable)){
# Update-Module $MyModuleName -ErrorAction Stop # -Credential $Credential
# } else {
# Install-Module $MyModuleName -Repository $MyRepoName -Scope CurrentUsers -ErrorAction Stop # -Credential $Credential
# }
# } catch {
# # But if something went wrong, just -Force it, hard.
# Install-Module $MyModuleName -Repository $MyRepoName -Scope CurrentUsers -Force -SkipPublisherCheck -AllowClobber # -Credential $Credential
# }
# https://winaero.com/blog/fix-install-module-missing-powershell/
Write-Host ''
Write-Host ''
Write-Host "`n========================================" -ForegroundColor Green
Write-Host ''
Write-Host '3. Install latest versions of selected Modules' -ForegroundColor Green
Write-Host ""
Write-Host ' Install some sample PowerShell Gallery Modules.' -ForegroundColor Yellow
Write-Host ' Also install the latest Custom-Tools.psm1 Module.' -ForegroundColor Yellow
Write-Host ''
Write-Host "========================================" -ForegroundColor Green
Write-Host ''
Write-Host ""
Write-Host "fimo *code* | select Name,Description " -ForegroundColor Yellow -NoNewLine ; Write-Host "# fimo => Find-Module. Accepts * and ? wildcards" -ForegroundColor Green
Write-Host "fimo PSCX | select * " -ForegroundColor Yellow -NoNewLine ; Write-Host "# Show all properties for the PSCX module" -ForegroundColor Green
Write-Host "fimo PSCX | select -expand Description " -ForegroundColor Yellow -NoNewLine ; Write-Host "# Show all info for a single property" -ForegroundColor Green
Write-Host "inmo PSCX " -ForegroundColor Yellow -NoNewLine ; Write-Host "# inmo => Install-Module" -ForegroundColor Green
Write-Host "gcm -module Posh-Gist " -ForegroundColor Yellow -NoNewLine ; Write-Host "# gcm => Get-Command" -ForegroundColor Green
Write-Host "gcm *ssh* " -ForegroundColor Yellow -NoNewLine ; Write-Host "# Show all commands containing 'ssh'" -ForegroundColor Green
Write-Host "Get-InstalledModule " -ForegroundColor Yellow -NoNewLine ; Write-Host "# " -ForegroundColor Green
Write-Host "gmo -ListAvailable " -ForegroundColor Yellow -NoNewLine ; Write-Host "# gmo => Get-Module. List all available modules on your computer" -ForegroundColor Green
Write-Host "gmo -All " -ForegroundColor Yellow -NoNewLine ; Write-Host "# gmo => Get-Module. List all loaded modules" -ForegroundColor Green
Write-Host '$env:PSModulePath ' -ForegroundColor Yellow -NoNewLine ; Write-Host "# To view all module paths that are loaded" -ForegroundColor Green
Write-Host 'Remove-Module PSCX ' -ForegroundColor Yellow -NoNewLine ; Write-Host "# Removes from current session only. Does not uninstall" -ForegroundColor Green
Write-Host 'Uninstall-Module PSCX ' -ForegroundColor Yellow -NoNewLine ; Write-Host "# Uninstall. Cannot uninstall if other modules have this as a dependency." -ForegroundColor Green
Write-Host ""
Write-Host "All Modules that are on `$env:PSModulePath are automatically loaded in all PowerShell sessions."
Write-Host "From PowerShell 3.0 onwards, only the Module header is loaded when a console is started, and the"
Write-Host "full Module is only loaded when a function is called from it, or a '#Requires -Modules' directive"
Write-Host "is in a script forcing it to be loaded. Can also use -Global switch to Import-Module cmdlet,"
Write-Host "to import the module into the global scope, allowing outside scripts to access their functionality."
Write-Host "http://msdn.microsoft.com/en-us/library/dd878284(v=vs.85).aspx"
Write-Host "The `$PSModulePath locations are currently:`n" ; Write-Host "$($env:PSModulePath -replace ";", "`n")" -ForegroundColor Yellow
Write-Host ""
# Write-Host "Note that as this script is running as Administrator, all of the installed Modules will be placed in:"
# Write-Host "C:\Program Files\WindowsPowerShell\Modules" -ForegroundColor Yellow
Write-Host ""
if ($PSVersionTable.PSVersion.Major -eq 2) {
Write-Host ''
Write-Host ''
Write-Host "====================" -ForegroundColor Red
Write-Host ''
Write-Host "Import-Module Cmdlet does not exist on PowerShell v2 so Module." -ForegroundColor Red
Write-Host "configuration will not continue. Configuration script will exit here." -ForegroundColor Red
Write-Host "You can rerun the configuration script once PowerShell is updated." -ForegroundColor Red
Write-Host ''
Write-Host "====================" -ForegroundColor Red
Write-Host ''
Write-Host ""
ThrowScriptErrorAndStop
}
Write-Host ""
if ($unattended -eq $false) { if ($(Confirm-Choice $confirm) -eq "no") { ThrowScriptErrorAndStop } }
# This approach is very, very fast and can be used to check against the versions of installed modules to see whether you are up-to-date.
function Get-PublishedModuleVersion($Name) {
# access the main module page, and add a random number to trick proxies
$url = "https://www.powershellgallery.com/packages/$Name/?dummy=$(Get-Random)"
$request = [System.Net.WebRequest]::Create($url)
# do not allow to redirect. The result is a "MovedPermanently"
$request.AllowAutoRedirect=$false
try
{
# send the request
$response = $request.GetResponse()
# get back the URL of the true destination page, and split off the version
$response.GetResponseHeader("Location").Split("/")[-1] -as [Version]
# make sure to clean up
$response.Close()
$response.Dispose()
}
catch
{
Write-Warning $_.Exception.Message
}
}
# Get-PublishedModuleVersion -Name ISESteroids
function Install-ModuleToDirectory {
[CmdletBinding()] [OutputType('System.Management.Automation.PSModuleInfo')]
param(
[Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] $Name,
[Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [ValidateScript({ Test-Path $_ })] $Destination
)
if (!(Test-Path $UserModulePath)) { New-Item $UserModulePath -ItemType Directory -Force }
if (($Profile -like "\\*") -and (Test-Path (Join-Path $UserModulePath $Name))) {
if (Test-Administrator -eq $true) {
"remove module from network share and move to $Destination"
# Nothing in here will happen unless working on laptop with a network share
Uninstall-Module $Name -Force -Verbose
# Install-Module $MyModule -Scope CurrentUser -Force -Verbose # This will *always* install to network share if in use so use Save-Module
Find-Module -Name $Name -Repository 'PSGallery' | Save-Module -Path $Destination # Install the module to the custom destination.
Import-Module -FullyQualifiedName (Join-Path $Destination $Name)
}
else {
Write-Host "Module found on network share module path but need to be administrator and connected to VPN" -ForegroundColor Yellow -BackgroundColor Black
Write-Host "to correctly move Modules into the users module folder on C:\" -ForegroundColor Yellow -BackgroundColor Black
}
}
elseif (Test-Path (Join-Path $AdminModulesPath $Name)) {
if (Test-Administrator -eq $true) {
"remove module from $AdminModulesPath and move to $Destination"
Uninstall-Module $Name -Force -Verbose
# Install-Module $MyModule -Scope CurrentUser -Force -Verbose # This will *always* install to network share if in use so use Save-Module
Find-Module -Name $Name -Repository 'PSGallery' | Save-Module -Path $Destination # Install the module to the custom destination.
Import-Module -FullyQualifiedName (Join-Path $Destination $Name)
}
else {
Write-Host "Module found on in Admin Modules folder: $(split-path $AdminModulesPath) C:\Program Files\WindowsPowerShell\Modules." -ForegroundColor Yellow -BackgroundColor Black
Write-Host "Need to be Admin to correctly move Modules into the users module folder on C:\" -ForegroundColor Yellow -BackgroundColor Black
}
}
# Get-InstalledModule # Shows only the Modules installed by PowerShellGet.
# Get-Module # Gets the modules that have been imported or that can be imported into the current session.
elseif (Test-Path (Join-Path $Destination $Name)) {
# https://stackoverflow.com/questions/48424152/compare-system-version-in-powershell
# To use the repository, you either need PowerShell 5 or install the PowerShellGet module manually (which is
# available for download on powershellgallery.com) to get Find/Save/Install/Update/Remove-Script for Modules.
# https://community.idera.com/database-tools/powershell/powertips/b/tips/posts/getting-latest-powershell-gallery-module-version
# https://stackoverflow.com/questions/52633919/powershell-sort-version-objects-descending
# "1.." -match "\b\d(\.\d{0,5}){0,3}\d$"
# https://techibee.com/powershell/check-if-a-string-contains-numbers-in-it-using-powershell/2842
$ModVerLocal = (Get-Module $Name -ListAvailable -EA Silent).Version
$ModVerOnline = Get-PublishedModuleVersion $Name
$ModVerLocal = "$(($ModVerLocal).Major).$(($ModVerLocal).Minor).$(($ModVerLocal).Build)" # reuse the [version] variable as a [string]
$ModVerOnline = "$(($ModverOnline).Major).$(($ModverOnline).Minor).$(($ModverOnline).Build)" # reuse the [version] variable as a [string]
# if ($ModuleVersionOnline -ne "") { $ModuleVersionOnline = "$($ModuleVersionOnline.split(".")[0]).$($ModuleVersionOnline.split(".")[1]).$($ModuleVersionOnline.split(".")[2])" }
echo "Local Version: $ModVerLocal"
echo "Online Version: $ModVerOnline"
if ($ModVerLocal -eq $ModVerOnline) {
echo "$Name is installed and latest version, nothing to do!"
}
else {
if ([bool](Get-Module $Name) -eq $true) { Uninstall-Module $Name -Force -Verbose }
rm (Join-Path $Destination $Name) -Force -Recurse -Verbose
Find-Module -Name $Name -Repository 'PSGallery' | Save-Module -Path $Destination -Force -Verbose # Install the module to the custom destination.
Import-Module -FullyQualifiedName (Join-Path $Destination $Name) -Force -Verbose
}
}
else { # Final case is no module is in network share, or local admin modules, or local user modules so now just install it
Get-PublishedModuleVersion $Name
Find-Module -Name $Name -Repository 'PSGallery' | Save-Module -Path $Destination -Force -Verbose # Install the module to the custom destination.
Import-Module -FullyQualifiedName (Join-Path $Destination $Name) -Force -Verbose
}
# Finally, output the Path to the newly installed module and the functions contained in it
(Get-Module $Name | select Path).Path
$out = ""; foreach ($i in (Get-Command -Module $Name).Name) {$out = "$out, $i"} ; "" ; Write-Wrap $out.trimstart(", ") ; ""
# return (Get-Module)
}
# Force installation to User Modules, even if running as Admin. i.e. keep all installations in User space.
# Default user space location for Modules is here, but if Admin, it will try to install to C:\Program Files\WindowsPowerShell
# We want to force installation into user space, but there is no command to do this
# There is a command to force installation to AllUser space: Install-Module <Name> -Scope AllUsers
# $ModulePath = "$(Split-Path $Profile)\Modules1" # This is where all modules must go. It should be on path (must add if required)
# $ModulePathTest = foreach ($i in ($env:PSModulePath).split(";")) { if ($i -like $ModulePath) { $True } } # Get
# if ($ModulePathTest -eq $null) { } # Need to add this if not present
Write-Host "WARNING: Some errors may happen in the below as Modules might have functions that clash" -ForegroundColor Yellow -BackgroundColor Black
Write-Host "with existing Cmdlets and Functions. If so, note those that you want to force and then" -ForegroundColor Yellow -BackgroundColor Black
Write-Host "rerun with the '-AllowClobber' switch if you want to give priority to a given Module." -ForegroundColor Yellow -BackgroundColor Black
Write-Host ""
# https://www.reddit.com/r/PowerShell/comments/2l9itf/useful_module_megathread/
# https://rkeithhill.wordpress.com/2013/10/18/psreadline-a-better-line-editing-experience-for-the-powershell-console/
# https://devblogs.microsoft.com/scripting/the-search-for-a-better-powershell-console-experience/
# https://haacked.com/archive/2011/04/19/writing-a-nuget-package-that-adds-a-command-to-the.aspx/
Write-Host ''
Write-Host "Install NuGet Provider" -ForegroundColor Yellow -BackgroundColor Black
Write-Host "Note: this is required for various modules so want this at latest version (at least 2.8.5.201)."
Write-Host "Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser"
if ($PSver -gt 4) { Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser }
# Get-PackageProvider | where Name -eq "NuGet" | select Version | select -ExpandProperty Version
Write-Host '' -ForegroundColor Green
Write-Host 'General Module creation points:' -ForegroundColor Green
Write-Host '- Create a folder named with the same name as the module psm1. e.g. "MyModule"'
Write-Host '- Create a file called MyModule.psm1 in that folder to hold your functions'
Write-Host '- Use New-ModuleManifest to create a MyModule.psd1 in that folder for the metadata'
Write-Host '- Update the ModuleRoot and FunctionsToExport properties in the MyModule.psd1'
Write-Host '- #Requires -Modules Module1,Module2,... directive in scripts that require Module functions'
Write-Host 'Note: Need to put "Export-ModuleMember -Alias * -Function *" to export aliases in the Module'
Write-Host 'Note: Need to use -Force on all of the below to make sure to re=import'
Write-Host 'Extremely good overview: https://powershellexplained.com/2017-05-27-Powershell-module-building-basics/'-ForegroundColor Magenta
Write-Host ''
Write-Host ''
Write-Host "Make sure that PowerShell Gallery (which is run by Microsoft) is Trusted ..." -ForegroundColor Yellow -BackgroundColor Black
if ((Get-PSRepository -Name PSGallery).InstallationPolicy -eq 'Trusted') { "PSGallery is already Trusted" }
else {
Write-Host "Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted"
if ($PSver -gt 4) { Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted } else { Write-Host "Must be PV v5 or higher to run this" -ForegroundColor Red }
}
# $dependencies = @{ # Taken this hash table and Module installer from https://github.com/pauby/PSTodoWarrior/blob/master/build.ps1
# InvokeBuild = 'latest'
# Configuration = 'latest'
# PowerShellBuild = 'latest'
# Pester = 'latest'
# PSScriptAnalyzer = 'latest'
# PSPesterTestHelpers = 'latest'
# PSDeploy = 'latest' # Maybe pin the version in case he breaks this...
# PSTodoTxt = 'latest'
# }
#
# # Dependencies
# if (-not (Get-Command -Name 'Get-PackageProvider' -EA silent)) {
# $null = Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser
# Write-Verbose 'Bootstrapping NuGet package provider.'
# Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null
# } elseif ((Get-PackageProvider).Name -notcontains 'nuget') {
# Write-Verbose 'Bootstrapping NuGet package provider.'
# Get-PackageProvider -Name NuGet -ForceBootstrap
# }
#
# # Trust the PSGallery is needed
# if ((Get-PSRepository -Name PSGallery).InstallationPolicy -ne 'Trusted') {
# Write-Verbose "Trusting PowerShellGallery."
# Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
# }
Write-Host ''
Write-Host "Install-Module PSReadLine" -ForegroundColor Yellow -BackgroundColor Black
# Write-Host " Out-Default"
Write-Host "View Module Contents: " -NoNewLine ; Write-Host "get-command -module psreadline" -ForegroundColor Yellow
Write-Host "Note: this is installed by default on Windows 10 but not on Windows 7. It is required for many"
Write-Host "console functions:"
Write-Host " - History searches with Ctrl+R."
Write-Host " - Type part of a command then F8 to see go to last matching command."
Write-Host " - Ctrl+Alt+Shif+? to see all PSReadLine shortcuts."
Write-Host "For Windows 10, there is nothing to do, but for Windows 7 (even with PowerShell 5.1)"
Write-Host "it must also be loaded into every session (unlike Windows 10 where it loads by default)."
Write-Host "A line in the profile extensions tests for Win 7 then imports PSReadLine if required."
if (-not (Get-Module -ListAvailable PSReadLine)) {
if ($PSver -gt 4) {
Install-Module PSReadLine -Scope CurrentUser -Force -Verbose
}
else {
Write-Host "Need to be on PS v5 or higher to run 'Install-Module PSReadLine'" -ForegroundColor Red
}
}
Import-Module PSReadLine -Scope Local -EA Silent
$x = ""; foreach ($i in (get-command -module PSReadLine).Name) {$x = "$x, $i"} ; "" ; Write-Wrap $x.trimstart(", ") ; ""
Write-Host ''
Write-Host "Install-Module PSScriptAnalyzer (Script Analysis Tool)" -ForegroundColor Yellow -BackgroundColor Black
# Write-Host " Get-ScriptAnalyzerRule, Invoke-Formatter, Invoke-ScriptAnalyzer"
Write-Host "View Module Contents: " -NoNewLine ; Write-Host "get-command -module posh-gist" -ForegroundColor Yellow
# if (-not (Get-Module -ListAvailable PSScriptAnalyzer)) { Install-Module PSScriptAnalyzer -Scope CurrentUser -Force -Verbose }
Install-ModuleToDirectory PSScriptAnalyzer $UserModulePath
Write-Host ''
Write-Host "Install-Module Sudo" -ForegroundColor Yellow -BackgroundColor Black
# Write-Host " New-SudoSession, Remove-SudoSession, Restore-OriginalSystemConfig, Start-SudoSession"
Write-Host "View Module Contents: " -NoNewLine ; Write-Host "get-command -module sudo" -ForegroundColor Yellow
# if (-not (Get-Module -ListAvailable Sudo)) { Install-Module Sudo -Scope CurrentUser -Force -Verbose }
Install-ModuleToDirectory Sudo $UserModulePath
Write-Host ''
Write-Host "Install-Module Posh-Git (Git Management Cmdlets)" -ForegroundColor Yellow -BackgroundColor Black
# Write-Host " Add-PoshGitToProfile, Add-SshKey, Enable-GitColors, Expand-GitCommand, Get-AliasPattern,"
# Write-Host " Get-GitBranch, Get-GitDirectory, Get-GitStatus, Get-PromptPath, Get-SshAgent, Get-SshPath,"
# Write-Host " Invoke-NullCoalescing, Start-SshAgent, Stop-SshAgent, TabExpansion, tgit, Update-AllBranches,"
Write-Host "Write-GitStatus, Write-Prompt, Write-VcsStatus"
Write-Host "View Module Contents: " -NoNewLine ; Write-Host "get-command -module posh-git" -ForegroundColor Yellow
# if (-not (Get-Module -ListAvailable Posh-Git)) { Install-Module Posh-Git -Scope CurrentUser -Force -Verbose }
Install-ModuleToDirectory Posh-Git $UserModulePath
Write-Host ''
Write-Host "Install-Module Posh-Gist (Gist Management Cmdlets)" -ForegroundColor Yellow -BackgroundColor Black
# Write-Host " Get-Gist, Get-GistCommits, Get-GistStar, New-Gist, Remove-Gist, Update-Gist"
Write-Host "View Module Contents: " -NoNewLine ; Write-Host "get-command -module posh-gist" -ForegroundColor Yellow
# if (-not (Get-Module -ListAvailable Posh-Gist)) { Install-Module Posh-Gist -Scope CurrentUser -Force -Verbose }
Install-ModuleToDirectory Posh-Gist $UserModulePath
Write-Host ''
Write-Host "Install-Module PowerShellForGitHub # (GitHub Management Cmdlets)" -ForegroundColor Yellow -BackgroundColor Black
Write-Host "This is a PowerShell module that provides command-line interaction and automation for the GitHub v3 API."
Write-Host "https://github.com/microsoft/PowerShellForGitHub/blob/master/USAGE.md#examples"
Write-Host "http://stevenmaglio.blogspot.com/2019/08/powershellforgithubadding-get.html"
Write-Host "https://wilsonmar.github.io/powershell-github/"
Write-Host "https://hant.kutu66.com/GitHub/article_142903 (need to translate)"
Write-Host "View Module Contents: " -NoNewLine ; Write-Host "get-command -module PowerShellForGitHub" -ForegroundColor Yellow
Install-ModuleToDirectory PowerShellForGitHub $UserModulePath
Write-Host ''
Write-Host "Install-Module Posh-SSH (SSH Cmdlets)" -ForegroundColor Yellow -BackgroundColor Black
# Write-Host " Get-PoshSSHModVersion, Get-SFTPChildItem, Get-SFTPContent, Get-SFTPLocation, Get-SFTPPathAttribute,"
# Write-Host " Get-SFTPSession, Get-SSHPortForward, Get-SSHSession, Get-SSHTrustedHost, Invoke-SSHCommand,"
# Write-Host " Invoke-SSHCommandStream, Invoke-SSHStreamExpectAction, Invoke-SSHStreamExpectSecureAction,"
# Write-Host " Invoke-SSHStreamShellCommand, Move-SFTPItem, New-SFTPFileStream, New-SFTPItem, New-SFTPSymlink,"
# Write-Host " New-SSHDynamicPortForward, New-SSHLocalPortForward, New-SSHRemotePortForward, New-SSHShellStream,"
# Write-Host " New-SSHTrustedHost, Remove-SFTPItem, Remove-SFTPSession, Remove-SSHSession, Remove-SSHTrustedHost,"
# Write-Host " Rename-SFTPFile, Set-SFTPContent, Set-SFTPLocation, Set-SFTPPathAttribute, Start-SSHPortForward,"
# Write-Host " Stop-SSHPortForward, Test-SFTPPath, Get-SCPFile, Get-SCPFolder, Get-SCPItem, Get-SFTPFile,"
# Write-Host " Get-SFTPItem, New-SFTPSession, New-SSHSession, Set-SCPFile, Set-SCPFolder, Set-SCPItem, Set-SFTPFile,"
# Write-Host " Set-SFTPFolder, Set-SFTPItem"
Write-Host "View Module Contents: " -NoNewLine ; Write-Host "get-command -module posh-ssh" -ForegroundColor Yellow -NoNewline ; Write-Host " # fimo *ssh* for other SSH tools" -ForegroundColor Green
# if (-not (Get-Module -ListAvailable Posh-SSH)) { Install-Module Posh-SSH -Scope CurrentUser -Force -Verbose }
# (gcm -mod posh-ssh | select Name | % { $_.Name + "," } | Out-String).replace("`r`n", " ").trim(", ")
Install-ModuleToDirectory Posh-SSH $UserModulePath
Write-Host ''
Write-Host "Install-Module PSColor (Color Get-ChildItem / gci / dir / ls output)" -ForegroundColor Yellow -BackgroundColor Black
# Write-Host " Out-Default"
Write-Host "View Module Contents: " -NoNewLine ; Write-Host "get-command -module pscolor" -ForegroundColor Yellow
Write-Host "Note: modifies Out-Default, so do not import by default, have setup 'color' function"
Write-Host "in profile extensions to activate this when required."
# if (-not (Get-Module -ListAvailable PSColor)) { Install-Module PSColor -Scope CurrentUser -Force -Verbose }
Install-ModuleToDirectory PSColor $UserModulePath
Write-Host ''
Write-Host "Install-Module Windows-ScreenFetch (System Utility)" -ForegroundColor Yellow -BackgroundColor Black
Write-Host "View Module Contents: " -NoNewLine ; Write-Host "get-command -module windows-screenfetch" -ForegroundColor Yellow
# if (-not (Get-Module -ListAvailable Windows-ScreenFetch)) { Install-Module Windows-ScreenFetch -Scope CurrentUser -Force -Verbose }
Install-ModuleToDirectory Windows-ScreenFetch $UserModulePath
Write-Host ''
Write-Host "Install-Module HistoryPx -AllowClobber (Enhanced Get-History tools)" -ForegroundColor Yellow -BackgroundColor Black
# Write-Host " Get-CaptureOutputConfiguration, Get-ExtendedHistoryConfiguration, Set-CaptureOutputConfiguration"
# Write-Host " Set-ExtendedHistoryConfiguration, Clear-History, Get-History, Out-Default"
Write-Host 'HistoryPx uses proxy commands to add extended history information to'
Write-Host 'PowerShell. This includes the duration of a command, a flag indicating whether'
Write-Host 'a command was successful or not, the output generated by a command (limited to'
Write-Host 'a configurable maximum value), the error generated by a command, and the'
Write-Host 'actual number of objects returned as output and as error records. HistoryPx'
Write-Host 'also adds a "__" variable to PowerShell that captures the last output that you'
Write-Host 'may have wanted to capture, and includes commands to configure how it decides'
Write-Host 'when output should be captured. Lastly, HistoryPx includes commands to manage'
Write-Host 'the memory footprint that is used by extended history information.'
Write-Host "View Module Contents: " -NoNewLine ; Write-Host "gcm -module historypx" -ForegroundColor Yellow
Write-Host "https://poshoholic.com/2014/10/30/making-history-more-fun-with-powershell/"
Write-Host "https://poshoholic.com/2014/10/21/transform-repetitive-script-blocks-into-invocable-snippets-with-snippetpx/"
Write-Host "https://poshoholic.com/2014/10/31/raise-your-powershell-game-with-historypx-debugpx-and-typepx/"
# if (-not (Get-Module -ListAvailable HistoryPx)) { Install-Module HistoryPx -AllowClobber -Scope CurrentUser -Force -Verbose }
Install-ModuleToDirectory HistoryPx $UserModulePath
Write-Host ''
Write-Host "Example of querying Modules" -ForegroundColor Yellow -BackgroundColor Black
Write-Host "Sort all available module sorting by version (which is 'Name' field in this view)"
Write-Host "Get-Module -ListAvailable | group version | sort Name -Descending" -ForegroundColor Yellow
# Get-Module -ListAvailable | group version | sort Name -Descending | Out-Host
Write-Host ''
Write-Host ''
Write-Host "To show more info for those at 2.0.0.0"
Write-Host "Get-Module -ListAvailable | ? { `$_.Version -eq '2.0.0.0' }" -ForegroundColor Yellow
# Get-Module -ListAvailable | ? { $_.version -eq '2.0.0.0' } | select Version,Name | sort -Descending | ft | Out-Host
Write-Host "More info on commands in a specific module from this view (e.g. the VpnClient Module):"
Write-Host "Get-Command -Module VpnClient | ft" -ForegroundColor Yellow
# Get-Command -Module VpnClient | ft | Out-Host
# Download various scripts to the $Profile Scripts folder
Write-Host ''
Write-Host ''
Write-Host "`n========================================" -ForegroundColor Green
Write-Host ''
Write-Host '4. Downoad latest versions of online scripts.' -ForegroundColor Green
Write-Host ''
Write-Host ' Place in the default PowerShell script folder and add to path' -ForegroundColor Yellow
Write-Host ' making them fully usable in any console. Can add more scripts' -ForegroundColor Yellow
Write-Host ' easily to be deployed to all systems at setup as required.' -ForegroundColor Yellow
Write-Host " C:\Users\$env:Username\Documents\WindowsPowerShell\Scripts" -ForegroundColor Cyan
Write-Host ''
Write-Host "========================================`n" -ForegroundColor Green
Write-Host ""
# $UserScriptsPath = "$(Split-Path $Profile)\Scripts" # Old path was "C:\ProgramData\Scripts"
mkdir $UserScriptsPath -Force | Out-Null
$RegistrySystemPath = 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' # System
$RegistryUserPath = "HKCU:\Environment"
$PathOld = (Get-ItemProperty -Path $RegistryUserPath -Name PATH).Path
$PathArray = $PathOld -Split ";" -replace "\\+$", ""
$FoundPath = 0
foreach ($Path in $PathArray) {
if ($Path -contains $UserScriptsPath ) {
$FoundPath = 1
}
}
if ($FoundPath -eq 0) {
$PathNew = $PathOld + ";" + $UserScriptsPath
Set-ItemProperty -Path $RegistryUserPath -Name PATH -Value $PathNew
(Get-ItemProperty -Path $RegistryUserPath -Name PATH).Path
}
function Download-Script ($url) {
$FileName = ($url -split "/")[-1] # Could also use: $url -split "/" | select -last 1 # 'hi there, how are you' -split '\s+' | select -last 1
$OutPath = Join-Path $UserScriptsPath $FileName
Write-Host "Downloading $FileName to $OutPath ..."
try { (New-Object System.Net.WebClient).DownloadString($url) | Out-File $OutPath }
catch { "Could not download $FileName ..." }
}
# function Download-Script ($url, $FinalName) {
# if ($url -eq '') { "Require URL to perform download." ; break}
# $DownloadName = ($url -split "/")[-1] # Could also use: $url -split "/" | select -last 1 # 'hi there, how are you' -split '\s+' | select -last 1
# $OutPath = Join-Path $ScriptsPath $DownloadName
# "Checking for '$OutPath' ..."
# if ($null -eq $FinalName) { $FinalName = $DownloadName }
# if (!(Test-Path "$ScriptsPath\$DownloadName") -and !(Test-Path "$ScriptName\$FinalName")) {
# "Downloading '$DownloadName' to '$OutPath' ..."
# try { (New-Object System.Net.WebClient).DownloadString($url) | Out-File $OutPath }
# catch { "Failed to download $FileName. Check internet connection, particularly TLS / VPN." }
# if ($FinalName -ne "") {
# if (Test-Path "$ScriptsPath\$DownloadName") {
# if (Test-Path "$ScriptPath\$FinalName") { Move-Item }
# Move-Item "$ScriptsPath\$DownloadName" "$ScriptsPath\$FinalName" -Force
# "Renamed '$DownloadName' to '$FinalName'"
# }
# }
# }
# }
# Can also install scripts with the *-Script Cmdlets
# Install-Script, Find-Script, Publish-Script, Save-Script, Uninstall-Script, Update-Script
# -Scope AllUsers => C:\Program Files\WindowsPowerShell\Scripts
# -Scope CurrentUser => C:\Users\$env:USERNAME\Documents\WindowsPowerShell\Scripts *or* ING VPN
# Due to the Corporate VPN issue, use the "Find-Script | Save-Script trick"
$UserScripts = "C:\Users\$env:USERNAME\Documents\WindowsPowerShell\Scripts"
Download-Script 'https://gallery.technet.microsoft.com/scriptcenter/Powershell-function-to-add-a7ac5229/file/166758/1/Add-Path.ps1'
# https://superwidgets.wordpress.com/2017/01/04/powershell-script-to-report-on-computer-inventory/
Download-Script 'https://gallery.technet.microsoft.com/scriptcenter/Powershell-Script-to-ping-15e0610a/file/127965/4/Ping-Report-v3.ps1'
if (Test-Path "$UserScriptsPath\Ping-Report-v3.ps1") { Move-Item "$UserScriptsPath\Ping-Report-v3.ps1" "$UserScriptsPath\Ping-Report.ps1" -Force } # remove the -v3 from filename
Download-Script 'https://gallery.technet.microsoft.com/scriptcenter/Fast-asynchronous-ping-IP-d0a5cf0e/file/124575/1/Ping-IPrange.ps1'
# mklement0 Tools: https://gist.github.com/mklement0/146f3202a810a74cb54a2d353ee4003f
# function Show-OperatorHelp { / function Show-TypeHelp { , Shows documentation for built-in .NET types, etc
Download-Script 'https://gist.githubusercontent.com/mklement0/146f3202a810a74cb54a2d353ee4003f/raw/044746494a61c212cad196a1a12c086e826ba719/Show-OperatorHelp.ps1'
Download-Script 'https://gist.githubusercontent.com/mklement0/50a1b101cd53978cd147b4b138fe6ef4/raw/9c4dfd2878dfdf8d74eccae707183cdfe536f436/Show-TypeHelp.ps1'
Download-Script 'https://gallery.technet.microsoft.com/scriptcenter/Check-for-Key-Presses-with-7349aadc/file/148286/2/Test-KeyPress.ps1'
# Set-Window.ps1
Download-Script 'https://gallery.technet.microsoft.com/scriptcenter/Set-the-position-and-size-54853527/file/146291/1/Set-Window.ps1'
# ConsoleArt demonstration script
Download-Script 'https://gist.github.com/shanenin/f164c483db513b88ce91/raw'
if (Test-Path "$UserScriptsPath\raw") { Move-Item "$UserScriptsPath\raw" "$UserScriptsPath\ConsoleArt.ps1" -Force }
# Registry
# Download-Script 'https://gallery.technet.microsoft.com/scriptcenter/Get-RegistryKeyLastWriteTim-63f4dd96/file/131343/1/Get-RegistryKeyLastWriteTime.ps1'
# try { Find-Script Get-RegistryKey | Save-Script -Path $UserScripts } catch { "Could not connect to PSGallery for Get-ReistryKey.ps1"}
### First run of Install-Script does the following:
# PATH Environment Variable Change
# Your system has not been configured with a default script installation path yet, which means you can only run a
# script by specifying the full path to the script file. This action places the script into the folder 'C:\Program
# Files\WindowsPowerShell\Scripts', and adds that folder to your PATH environment variable. Do you want to add the
# script installation path 'C:\Program Files\WindowsPowerShell\Scripts' to the PATH environment variable?
# [Y] Yes [N] No [S] Suspend [?] Help (default is "Y"): y
# ToDo: Archive all downloaded scripts in case links are broken
# ToDo: add try / throw for failure on download
Write-Host ''
Write-Host ''
Write-Host "`n========================================" -ForegroundColor Green
Write-Host ''
Write-Host '5. Install-BoxstarterPackage installations.' -ForegroundColor Green
Write-Host ""
Write-Host ' Call online script using Boxstarter to run through chocolately packages,' -ForegroundColor Yellow
Write-Host ' registry updates package installs and other system configuration tasks.' -ForegroundColor Yellow
Write-Host ' As using Boxstarter for this, the process will perform reboots when required.' -ForegroundColor Yellow
Write-Host ''
Write-Host "========================================`n" -ForegroundColor Green
Write-Host ""
Write-Host " Work in progress ..."
Write-Host " We might still be running in PowerShell v2 at this point which is not ideal."
Write-Host " Possible workaround: create a file in startup that kicks of the"
Write-Host " Install-BoxstarterPackage <url> after a reboot at this point. This can then"
Write-Host " handle all other tasks, windows updates, chocolatey package installs etc as"
Write-Host " the powershell session will come up in v5.1 after reboot and then it will be"
Write-Host " easier. But I don't know if this is the right approach ... would be good to"
Write-Host " get feedback from the Boxstarter maintainers."
Write-Host ""
Write-Host " How in general can I restart Things to get into v5.1, or, should I just run"
Write-Host " Boxstarter here and it will gracefully move from v2 to v5.1 as part of its"
Write-Host " normal operation? Need to test unless someone can advise me on that."
Write-Host ""
Write-Host ""
Write-Host "End of configuration" -ForegroundColor Red -BackgroundColor White
Write-Host ""
Write-Host ""
if (Get-Command Help-ToolkitConfig -EA Silent) { Help-ToolkitConfig } # Show the release notes held in Custom-Tools
Write-Host ""
Write-Host ""
Write-Host "Run 'Help-ToolkitConfig' to review the above notes."
Write-Host ""
Write-Host "Run 'Help-ToolkitCoreApps' to review important system apps to install."
Write-Host ""
# Clean up TEMP folder - no, don't do this, in case the scripts were deliberately run from this location
# if (Test-Path "$env:TEMP\BeginSystemConfig.ps1") { Remove-Item "$env:TEMP\BeginSystemConfig.ps1" -Force }
# if (Test-Path "$env:TEMP\ProfileExtensions.ps1") { Remove-Item "$env:TEMP\ProfileExtensions.ps1" -Force }
# if (Test-Path "$env:TEMP\Custom-Tools.psm1") { Remove-Item "$env:TEMP\Custom-Tools.psm1" -Force }
$hr = (Get-Date).Subtract($start_time).Hours ; $min = (Get-Date).Subtract($start_time).Minutes ; $sec = (Get-Date).Subtract($start_time).Seconds
if ($hr -ne 0) { $times += "$hr hr " } ; if ($min -ne 0) { $times += "$min min " } ; $times += "$sec sec"
"`nScript took $times to complete.`n" # $((Get-Date).Subtract($start_time).TotalSeconds)
if ( $MyInvocation.InvocationName -eq 'BeginSystemConfig.ps1') { "`nWarning: Toolkit configuration was not dotsourced, so ProfileExtensions will not be active. Either restart a new PowerShell session or rerun as dotsourced:`n`n . BeginSystemConfig.ps1`n" }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment