Skip to content

Instantly share code, notes, and snippets.

@eplord
Forked from techthoughts2/ws_setup.ps1
Created March 2, 2025 08:29
Show Gist options
  • Save eplord/f987f1260453e92f00bb0a6eb2ab3371 to your computer and use it in GitHub Desktop.
Save eplord/f987f1260453e92f00bb0a6eb2ab3371 to your computer and use it in GitHub Desktop.
This PowerShell script will setup a fresh workstation with everything needed to sucessfully work and be a DevOps Master day-to-day.
#region installs
# core tech choco installs
$script:chocoCoreTech = @(
# 'vscode' # visual studio code
# 'python' # python
# '7zip' # file archiver with good compression ratio
# 'git' # git for windows
# 'firefox' # firefox browser
)
# core tech winget installs
$script:wingetCoreTech = @(
'Git.Git' # git for windows
# 'Microsoft.VisualStudioCode' # visual studio code
'Microsoft.VisualStudioCode.Insiders' # visual studio code insiders
'7zip.7zip' # file archiver with good compression ratio
'Mozilla.Firefox' # firefox browser
'JanDeDobbeleer.OhMyPosh' # oh my posh
'Microsoft.PowerShell' # powershell
)
# basic choco installs
$script:chocoSoftwareInstalls = @(
# 'ytmdesktop' #https://github.com/ytmdesktop/ytmdesktop/issues/563
# 'spotify' # spotify
# 'cacher' # code snippet organizer
# 'winscp' # Open source free SFTP client, SCP client, FTPS client and FTP client
# 'telegram' # Cloud-based synchronized messaging app with a focus on speed and security
# 'grepwin' # powerful and fast search tool using regular expressions.
# 'notepadplusplus' # source code editor and Notepad replacement
# 'googlechrome' # chrome browser
# 'foxitreader' # pdf client
'paint.net' # photo editor
)
# basic winget installs
$script:wingetSoftwareInstalls = @(
'Spotify.Spotify' # spotify
'PenguinLabs.Cacher' # code snippet organizer
'WinSCP.WinSCP' # Open source free SFTP client, SCP client, FTPS client and FTP client
'Telegram.TelegramDesktop' # Cloud-based synchronized messaging app with a focus on speed and security
'StefansTools.grepWin' # powerful and fast search tool using regular expressions.
'Notepad++.Notepad++' # source code editor and Notepad replacement
# 'Google.Chrome' # chrome browser
# 'Foxit.FoxitReader' # pdf client
'JGraph.Draw' # draw.io
'WinDirStat.WinDirStat' # disk usage analyzer
'NickeManarin.ScreenToGif' # screen recorder to gif
'DominikReichl.KeePass' #KeePass
'Quicken.Quicken' #Quicken
'Piriform.CCleaner' #CCleaner
'ArobasMusic.GuitarPro.8' #GuitarPro
'PrivateInternetAccess.PrivateInternetAccess' #Private Internet Access
'Valve.Steam' #Steam
'Synology.DriveClient' #Synology Drive Client
'Appest.TickTick' #TickTick
'VideoLAN.VLC' #VLC
'Symless.Synergy' #Synergy
)
# choco azure installs
$script:chocoInstallsAzure = @(
# 'azure-cli' # cli for azure
# 'AzureStorageExplorer' # the azure storage explorer
# 'azure-functions-core-tools-3' # core tool set for local dev of azure functions
'azcopy10' # azure copy tool
# 'bicep' # bicep cli
)
# winget azure installs
$script:wingetInstallsAzure = @(
'Microsoft.AzureCLI' # cli for azure
'Microsoft.AzureStorageExplorer' # the azure storage explorer
'Microsoft.AzureFunctionsCoreTools' # core tool set for local dev of azure functions
'Microsoft.AzureStorageEmulator'
'Microsoft.Bicep' # bicep cli
)
# choco aws installs
$script:chocoInstallsAWS = @(
'aws-vault' # A tool to securely store and access AWS credentials in a development environment
# 'awscli' # aws cli
)
# winget aws installs
$script:wingetInstallsAWS = @(
'Amazon.AWSCLI' # aws cli
'Amazon.SAM-CLI' # aws sam cli
'Amazon.SessionManagerPlugin' # aws session manager plugin
)
# winget aws cdk typescript installs
$script:wingetAWSCDKTypeScript = @(
'Amazon.AWSCLI' # aws cli
'OpenJS.NodeJS' # nodejs
)
# core python installs
$script:pythonCore = @(
'Python.Python.3.13' #python
)
# a list of useful Azure PowerShell modules
$script:azureModules = @(
'Az' # standard Azure modules
'AzureDevOps' # interact with the Azure DevOps REST API.
'AzurePipelinesPS' # makes interfacing with Azure Pipelines a bit easier.
'CosmosDB' # provides cmdlets for working with Azure Cosmos DB.
'PSArm' # experimental DSL for ARM templates (based on bicep)
'Bicep' # enable the features provided by the Bicep CLI in PowerShell.
)
# a list of useful AWS PowerShell modules
$script:awsModules = @(
'AWS.Tools.Common'
'AWS.Tools.CloudWatch'
'AWS.Tools.CostExplorer'
'AWS.Tools.S3'
'AWS.Tools.SecretsManager'
'AWS.Tools.SecurityToken'
'AWS.Tools.SQS'
)
# a list of useful Core PowerShell modules
$script:modules = @(
@{
ModuleName = 'Catesta'
Version = 'Latest'
}
@{
ModuleName = 'pwshEmojiExplorer'
Version = 'Latest'
}
@{
ModuleName = 'pwshPlaces'
Version = 'Latest'
}
@{
ModuleName = 'Convert'
Version = 'Latest'
}
@{
ModuleName = 'DnsClient-PS'
Version = 'Latest'
}
@{
ModuleName = 'InvokeBuild'
Version = 'Latest'
}
@{
ModuleName = 'Pester'
Version = 'Latest'
}
@{
ModuleName = 'platyPS'
Version = '0.12.0'
}
@{
ModuleName = 'posh-git'
Version = 'Latest'
}
@{
ModuleName = 'PoshGram'
Version = 'Latest'
}
@{
ModuleName = 'PSReadline'
Version = 'Latest'
}
@{
ModuleName = 'PSScriptAnalyzer'
Version = 'Latest'
}
@{
ModuleName = 'PSWordCloud'
Version = 'Latest'
}
@{
ModuleName = 'Terminal-Icons'
Version = 'Latest'
}
)
# python VSCode extensions
$script:vscodeExtensionsPython = @(
'almenon.arepl' #AREPL automatically evaluates python code in real-time as you type.
'formulahendry.code-runner' #Run code snippet or code file for multiple languages:
'ms-python.python' #python
'ms-python.vscode-pylance' #Fast, feature-rich language support for Python
'ms-toolsai.jupyter' #basic notebook support for language kernels
'njpwerner.autodocstring' #quickly generate docstrings for python functions.
)
# aws VSCode extensions
$script:vscodeExtensionsAWS = @(
'amazonwebservices.aws-toolkit-vscode' #AWS Toolkit is an extension for Visual Studio Code that enables you to interact with Amazon Web Services (AWS).
'aws-scripting-guy.cform' #CloudFormation support
'DanielThielking.aws-cloudformation-yaml' #This extension adds some snippets to YAML based files for AWS CloudFormation.
'kddejong.vscode-cfn-lint' #VS Code CloudFormation Linter uses cfn-lint to lint your CloudFormation templates.
)
# aws cdktf VSCode extensions
$script:vscodeExtensionsAWSCDK = @(
'dbaeumer.vscode-eslint' #Integrates ESLint JavaScript into VS Code.
)
# azure VSCode extensions
$script:vscodeExtensionsAzure = @(
'damienaicheh.azure-devops-snippets' #Azure DevOps snippets
'ms-dotnettools.vscode-dotnet-runtime' #.NET Install Tool for Extension Authors - dependency
'ms-azure-devops.azure-pipelines' #Syntax highlighting, IntelliSense, and more for Azure Pipelines YAML
'ms-azuretools.vscode-azureresourcegroups' #View and manage Azure resources directly from VS Code.
'ms-azuretools.vscode-azurefunctions' #Use the Azure Functions extension to quickly create, debug, manage, and deploy serverless apps directly from VS Code.
'msazurermtools.azurerm-vscode-tools' #The Azure Resource Manager (ARM) Tools for Visual Studio Code
'ms-azuretools.vscode-bicep' #Bicep language support
'ms-vscode.azure-account' #The Azure Account extension provides a single Azure sign-in
'ms-vscode.azurecli' #Scrapbooks for developing and running commands with the Azure CLI.
)
# core VSCode extensions
$script:vscodeExtensions = @(
'aaron-bond.better-comments' #The Better Comments extension will help you create more human-friendly comments in your code.
'davidanson.vscode-markdownlint' #Markdown/CommonMark linting and style checking
'DotJoshJohnson.xml' #xml tools
'eamodio.gitlens' #GitLens supercharges the Git capabilities
'emilast.LogFileHighlighter' #Adds color highlighting to log files
# 'GitHub.copilot' #AI pair programmer from GitHub
'hediet.vscode-drawio' #This unofficial extension integrates Draw.io
'mechatroner.rainbow-csv' #Highlight columns in comma (.csv), tab (.tsv), semicolon and pipe
# 'ms-dotnettools.csharp' #Welcome to the C# extension for Visual Studio Code!
'ms-vscode.powershell' #PowerShell!
'oderwat.indent-rainbow' #make indentation more readable
'PKief.material-icon-theme' #material icon theme for icons
'redhat.vscode-yaml' #Provides comprehensive YAML Language support - dependency
'ryanluker.vscode-coverage-gutters' #codecoverage indicator
'ryu1kn.partial-diff' #You can compare (diff) text selections within a file, across different files, or to the clipboard
'SirTori.indenticator' #Visually highlights the current indent depth.
'streetsidesoftware.code-spell-checker' #A basic spell checker that works well with camelCase code.
'tuxtina.json2yaml' #Uses js-yaml to do the actual conversion json to yaml and vice-versa.
'usernamehw.errorlens' #ErrorLens turbo-charges language diagnostic features by making diagnostics stand out more prominently
'vangware.dark-plus-material' #theme
)
#endregion
#region paths
$script:loggerDirectory = 'C:\wsetup\wsetup-.log'
$script:tempPath = -join ($env:TEMP, '\ws_setup')
$script:vscodeSettingsPath = -join ($env:APPDATA, '\Code - Insiders\User')
$script:vscodeSnippetsPath = -join ($env:APPDATA, '\Code - Insiders\User\snippets')
$script:windowsTerminalSettingsPath = -join ($env:LOCALAPPDATA, '\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState')
$script:windowsTerminalBackgroundPath = -join ($env:LOCALAPPDATA, '\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\RoamingState')
# $script:profilePath = -join ($env:USERPROFILE, '\Documents\PowerShell')
# $script:profilePath = $profile.CurrentUserAllHosts
$script:ohmyposhSettings = $env:USERPROFILE
#endregion
#region content locations
$gistUrl = 'https://api.github.com/gists/a208d2bd924691bae7ec7904cab0bd8e'
$script:psProfile = 'https://api.github.com/gists/2b4d8e590a7fa41f32a76013df664020'
$script:vsCodeSettingsJSON = 'https://api.github.com/gists/d0997337224510743e2072dc5c343363' #settings.json
$script:vsCodePythonSnippetsJSON = 'https://api.github.com/gists/042b2b47e7a94c1a5a2bc79439a4fb81' #vscode_python_snippets.json
$script:vsCodePowerShellSnippetsJSON = 'https://api.github.com/gists/9ec91ab0ca26f96cf7ca4842053fa8fb' #vscode_ps_snippets.json
$script:windowsTerminalSettingsJSON = 'https://api.github.com/gists/df416a8df55c6c4009c9dcd337d4c8cf' #settings.json
# $script:ohmyposhJSON = 'https://api.github.com/gists/da99b8255a8ca720430d188f649a9bd7' #.jake.omp.json
$script:ohmyposhYAML = 'https://api.github.com/gists/2ff212e18f8019ab69b7f1bfb9659e79' #.jake.omp.yml
$script:setupFiles = 'https://tt-ws.s3-us-west-2.amazonaws.com/ws.zip' #zip containing background images and fonts
#endregion
#region supporting functions
<#
.SYNOPSIS
Evaluates if chocolatey is installed
#>
function Test-Choco {
[CmdletBinding()]
param (
)
$result = $true #assume the best
Write-InfoLog 'Test-Choco'
$testchoco = pwsh -noprofile -c 'choco -v'
Write-InfoLog $testchoco
if ($testchoco[0] -like '*.*.*' -and $testchoco[0] -notlike '*not*recognized*') {
Write-InfoLog ('Chocolatey Version: {0}' -f $testchoco)
}
else {
Write-WarningLog 'Chocolatey is not installed.'
$result = $false
}
return $result
} #Test-Choco
<#
.SYNOPSIS
Evaluates if WinGet is installed
#>
function Test-WinGet {
[CmdletBinding()]
param (
)
Write-InfoLog 'Test-WinGet'
$result = $true #assume the best
$testwinget = pwsh -noprofile -c 'winget -v'
Write-InfoLog $testwinget
if ($testwinget[0] -like 'v*') {
Write-InfoLog ('winget Version: {0}' -f $testwinget)
}
else {
Write-WarningLog 'winget is not installed.'
$result = $false
}
return $result
} #Test-WinGet
<#
.SYNOPSIS
Installs chocolatey
#>
function Install-Choco {
[CmdletBinding()]
param (
)
Write-InfoLog 'Install-Choco'
Write-InfoLog 'Installing Chocolately...'
Write-InfoLog 'Setting execution and security settings...'
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
try {
Write-InfoLog 'Downloading and installing...'
Invoke-WebRequest https://chocolatey.org/install.ps1 -UseBasicParsing -ErrorAction Stop | Invoke-Expression -ErrorAction Stop
Write-InfoLog 'INSTALL COMPLETE.'
}
catch {
Write-ErrorLog $_
Close-Logger
throw $_
}
} #InstallChoco
<#
.SYNOPSIS
Installs Azure related PowerShell modules
#>
function Install-HelpfulAzureModules {
[CmdletBinding()]
param (
)
Write-InfoLog 'Install-HelpfulAzureModules'
Write-InfoLog 'Evaluating Azure modules...'
foreach ($module in $script:azureModules) {
Write-InfoLog (' {0} evaluating...' -f $module)
if (-not (Get-Module $module -ListAvailable)) {
if ($module -eq 'PSArm') {
Write-InfoLog (' Installing {0}' -f $module)
Install-Module -Name $module -Scope CurrentUser -Repository PSGallery -AllowPrerelease -Force -AllowClobber
}
else {
Write-InfoLog (' Installing {0}' -f $module)
Install-Module -Name $module -Scope CurrentUser -Repository PSGallery -Force
}
}
else {
Write-InfoLog (' {0} VERIFIED. NO ACTION TAKEN.' -f $module)
}
}
} #Install-HelpfulAzureModules
<#
.SYNOPSIS
Installs Core PowerShell modules
#>
function Install-BaseModules {
[CmdletBinding()]
param (
)
Write-InfoLog 'Install-BaseModules'
Write-InfoLog 'Evaluating modules...'
foreach ($module in $script:modules) {
Write-InfoLog (' {0} evaluating...' -f $module.ModuleName)
if ($module.Version -like '*beta*' -or $module.Version -like '*rc*') {
Write-InfoLog (' {0} version check...' -f $module.Version)
$moduleEval = Get-InstalledModule -Name $module.ModuleName -RequiredVersion $module.Version -AllowPrerelease -ErrorAction SilentlyContinue
}
elseif ($module.Version -ne 'Latest') {
Write-InfoLog (' {0} version check...' -f $module.Version)
$moduleEval = Get-InstalledModule -Name $module.ModuleName -RequiredVersion $module.Version -ErrorAction SilentlyContinue
}
else {
$moduleEval = Get-Module -Name $module.ModuleName -ListAvailable -ErrorAction SilentlyContinue
}
if (-not $moduleEval) {
if ($module.Version -like '*beta*' -or $module.Version -like '*rc*') {
Write-InfoLog (' Installing {0} - {1}' -f $module.ModuleName, $module.Version)
Install-Module -Name $module.ModuleName -Scope CurrentUser -Repository PSGallery -RequiredVersion $module.Version -Force -AllowPrerelease
}
elseif ($module.Version -ne 'Latest') {
Write-InfoLog (' Installing {0} - {1}' -f $module.ModuleName, $module.Version)
Install-Module -Name $module.ModuleName -Scope CurrentUser -Repository PSGallery -RequiredVersion $module.Version -Force
}
else {
Write-InfoLog (' Installing {0}' -f $module.ModuleName)
Install-Module -Name $module.ModuleName -Scope CurrentUser -Repository PSGallery -Force
}
}
else {
Write-InfoLog (' {0} VERIFIED. NO ACTION TAKEN.' -f $module.ModuleName)
}
}
} #Install-BaseModules
<#
.SYNOPSIS
Uninstalls older versions of Pester that ship with Windows
#>
function Uninstall-Pester ([switch]$All) {
Write-InfoLog 'Uninstall-Pester'
if ([IntPtr]::Size * 8 -ne 64) {
Write-ErrorLog 'Run this script from 64bit PowerShell.'
throw 'Run this script from 64bit PowerShell.'
}
$pesterPaths = foreach ($programFiles in ($env:ProgramFiles, ${env:ProgramFiles(x86)})) {
$path = '{0}\WindowsPowerShell\Modules\Pester' -f $programFiles
if ($null -ne $programFiles -and (Test-Path $path)) {
if ($All) {
Get-Item $path
}
else {
Get-ChildItem "$path\3.*"
}
}
}
if (-not $pesterPaths) {
"There are no Pester$(if (-not $all) {" 3"}) installations in Program Files and Program Files (x86) doing nothing."
return
}
foreach ($pesterPath in $pesterPaths) {
takeown /F $pesterPath /A /R
icacls $pesterPath /reset
# grant permissions to Administrators group, but use SID to do
# it because it is localized on non-us installations of Windows
icacls $pesterPath /grant "*S-1-5-32-544:F" /inheritance:d /T
Remove-Item -Path $pesterPath -Recurse -Force -Confirm:$false
}
} #Uninstall-Pester
<#
.SYNOPSIS
Installs VSCode extension
#>
function Install-VSCodeExtension {
[CmdletBinding()]
param (
[string[]]$ExtensionList
)
Write-InfoLog 'Install-VSCodeExtension'
foreach ($Extension in $ExtensionList) {
Write-InfoLog ('Installing {0}...' -f $Extension)
code-insiders --install-extension $Extension
}
}
<#
.SYNOPSIS
Evaluates if a package is installed with choco
#>
function Test-ChocoInstall {
[CmdletBinding()]
param (
# choco package to be checked for
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$ChocoPackage
)
Write-InfoLog 'Test-ChocoInstall'
$result = $true #assume the best
$eval = $null
Write-InfoLog ('Checking for {0}...' -f $ChocoPackage)
$eval = pwsh -noprofile -c "choco list --localonly $ChocoPackage"
if ($eval -match $ChocoPackage) {
Write-InfoLog 'Package VERIFIED.'
}
else {
Write-InfoLog 'Package NOT FOUND'
$result = $false
}
return $result
} #Test-ChocoInstall
<#
.SYNOPSIS
Install choco package
#>
function Install-ChocoPackage {
[CmdletBinding()]
param (
# choco package to be installed
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$ChocoPackage
)
Write-InfoLog 'Install-ChocoPackage'
Write-InfoLog ('Choco installing - {0}' -f $ChocoPackage)
pwsh -noprofile -c "choco install $ChocoPackage -y"
} #Install-ChocoPackage
<#
.SYNOPSIS
Evaluates if a package is installed with choco
#>
function Test-WingetInstall {
[CmdletBinding()]
param (
# winget package to be checked for
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$WingetPackage
)
Write-InfoLog 'Test-WingetInstall'
$result = $true #assume the best
$eval = $null
Write-InfoLog ('Checking for {0}...' -f $WingetPackage)
$eval = pwsh -noprofile -c "winget list --id=$WingetPackage --exact --accept-source-agreements"
if ($eval -like "*$WingetPackage*") {
Write-InfoLog 'Package VERIFIED.'
}
else {
Write-InfoLog 'Package NOT FOUND'
$result = $false
}
return $result
} #Test-WingetInstall
<#
.SYNOPSIS
Install winget package
#>
function Install-WingetPackage {
[CmdletBinding()]
param (
# winget package to be installed
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$WingetPackage
)
Write-InfoLog 'Install-WingetPackage'
Write-InfoLog ('winget installing - {0}' -f $WingetPackage)
pwsh -noprofile -c "winget install --id=$WingetPackage --silent --accept-package-agreements --accept-source-agreements"
} #Install-WingetPackage
<#
.SYNOPSIS
Evaluates if workstation setup files are present. If not downloads and unzips.
#>
function Get-WSSetupFiles {
[CmdletBinding()]
param (
)
Write-InfoLog 'Get-WSSetupFiles'
$wsFilesPath = -join ($script:tempPath, '\ws_files')
Write-InfoLog ('Evaluating if {0} is present...' -f $wsFilesPath)
if (-not (Test-Path $wsFilesPath)) {
Write-InfoLog ' Downloading workstation setup files...'
try {
$invokeSplat = @{
Uri = $script:setupFiles
OutFile = "$script:tempPath\ws.zip"
ErrorAction = 'Stop'
}
Invoke-WebRequest @invokeSplat
Write-InfoLog ' Download complete.'
}
catch {
Write-ErrorLog $_
Close-Logger
throw 'Download failed.'
}
try {
Write-InfoLog ' Expanding zip download...'
$expandSplat = @{
LiteralPath = "$script:tempPath\ws.zip"
DestinationPath = $wsFilesPath
ErrorAction = 'Stop'
}
Expand-Archive @expandSplat
Write-InfoLog ' UNZIPPED!'
}
catch {
Write-ErrorLog $_
Close-Logger
throw 'Unzip failed.'
}
}
else {
Write-InfoLog " VERIFIED. No action taken."
}
} #Get-WSSetupFiles
<#
.SYNOPSIS
Retrieves settings files from github and places in appropriate locations
#>
function Set-SettingsFiles {
[CmdletBinding()]
param (
)
Write-InfoLog 'Set-SettingsFiles'
$settings = @(
@{
Name = 'VSCode Settings'
URI = $script:vsCodeSettingsJSON
File = -join ($script:vscodeSettingsPath, '\settings.json')
}
@{
Name = 'VSCode Python Snippets'
URI = $script:vsCodePythonSnippetsJSON
File = -join ($script:vscodeSnippetsPath, '\python.json')
}
@{
Name = 'VSCode PowerShell Snippets'
URI = $script:vsCodePowerShellSnippetsJSON
File = -join ($script:vscodeSnippetsPath, '\powershell.json')
}
@{
Name = 'Windows Terminal Settings'
URI = $script:windowsTerminalSettingsJSON
File = -join ($script:windowsTerminalSettingsPath, '\settings.json')
}
# @{
# Name = 'oh-my-posh Settings'
# URI = $script:ohmyposhJSON
# File = -join ($script:ohmyposhSettings, '\.jake.omp.json')
# }
@{
Name = 'oh-my-posh YAML Settings'
URI = $script:ohmyposhYAML
File = -join ($script:ohmyposhSettings, '\.jake.omp.yml')
}
@{
Name = 'PowerShell profile'
URI = $script:psProfile
# File = -join ($script:profilePath, '\profile.ps1')
File = $profile.CurrentUserAllHosts
}
)
foreach ($setting in $settings) {
Start-Sleep -Milliseconds 500
Write-InfoLog ('Downloading {0} to {1}' -f $setting.Name, $setting.File)
$gistUrl = $null
$fileName = $null
$gistContent = $null
$gistUrl = $setting.URI
$fileName = Split-Path $setting.File -leaf
# $path = 'C:\rs-pkgs\settings'
try {
$invokeSplat = @{
Uri = $gistUrl
ErrorAction = 'Stop'
}
$gist = Invoke-RestMethod @invokeSplat
$gistContent = $gist.Files.$fileName.Content
Write-InfoLog ' Download COMPLETED.'
}
catch {
Write-ErrorLog $_
Close-Logger
continue
}
try {
Write-InfoLog ' Writing out content...'
$setContentSplat = @{
# Path = "$path\$fileName"
Path = $setting.File
Value = $gistContent
Confirm = $false
Force = $true
ErrorAction = 'Stop'
}
Set-Content @setContentSplat
Write-InfoLog ' Setting applied!'
}
catch {
Write-ErrorLog $_
Close-Logger
continue
}
}
} #Set-SettingsFiles
<#
.SYNOPSIS
Installs all specified fonts in fonts folder
.NOTES
Uses the Add-Font.ps1 script contained in the ws.zip file
https://www.nerdfonts.com/font-downloads - updated font packages that have special characters like AWS icons
https://github.com/microsoft/cascadia-code/releases - official Cascadia Code font - does not have special characters
#>
function Install-Fonts {
[CmdletBinding()]
param (
)
Write-InfoLog 'Install-Fonts'
Write-InfoLog 'Starting fonts installation...'
$wsFilesPath = -join ($script:tempPath, '\ws_files\ws')
$fontPath = -join ($script:tempPath, '\ws_files\ws\Fonts\Fonts')
Write-InfoLog ' Getting required fonts to install...'
. $wsFilesPath\Fonts\PS_Font_Scripts\Add-Font.ps1 -Path $fontPath
} #Install-Fonts
<#
.SYNOPSIS
Copies all background images for Windows terminal
#>
function Set-BackImages {
[CmdletBinding()]
param (
)
Write-InfoLog 'Set-BackImages'
Write-InfoLog 'Starting Windows Terminal background files copy...'
$wsFilesPath = -join ($script:tempPath, '\ws_files\ws\backs')
Write-InfoLog ' Getting required backgrounds...'
$allBackgrounds = Get-ChildItem -Path $wsFilesPath
foreach ($background in $allBackgrounds) {
$testPath = -join ($script:windowsTerminalBackgroundPath, '\', $background.Name)
Write-InfoLog (' Evaluating - {0}' -f $background.FullName)
if (-not (Test-Path $testPath)) {
Write-InfoLog ' NOT found. Copying.'
Copy-Item -Path $background.FullName -Destination $testPath
}
else {
Write-InfoLog ' FOUND. No action taken.'
}
}
} #Set-BackImages
function Set-AzurePathVariables {
[CmdletBinding()]
param (
)
Write-InfoLog 'Set-AzurePathVariables'
$pathsToAdd = @(
'C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator'
'C:\Program Files (x86)\Microsoft SDKs\Azure\Azcopy'
)
Write-InfoLog 'Backing up curent paths to file...'
$fileName = -join ('path_', (Get-Date -format yyyy-MM-ddTHH-mm-ss-ff), '.txt')
[System.Environment]::GetEnvironmentVariable('PATH', 'machine') | Out-File "$script:tempPath\$fileName" -Force
Write-InfoLog 'Evaluating if current paths present...'
$paths = ($env:PATH).split(";")
foreach ($pathAdd in $pathsToAdd) {
$eval = $false
foreach ($path in $paths) {
if ($path -eq $pathAdd) {
$eval = $true
Write-InfoLog ( '{0} Found!' -f $pathAdd)
}
}
if ($eval -eq $false) {
Write-InfoLog ( '{0} NOT Found! Adding...' -f $pathAdd)
$oldPath = [System.Environment]::GetEnvironmentVariable('PATH', 'machine')
$newPath = "$OLDPATH;$pathAdd"
[Environment]::SetEnvironmentVariable("PATH", "$NEWPATH", "Machine")
}
else {
Write-InfoLog ' No Action taken'
}
}
} #Set-AzurePathVariables
#endregion
#region main
<#
.SYNOPSIS
Sets up a new workstation for desired development configuration
.DESCRIPTION
Downloads files, settings, and configurations to set new workstation to desired development setup and config.
.EXAMPLE
Invoke-WSSetup
Configures workstation to base level dev configuration.
.EXAMPLE
Invoke-WSSetup -Python -AWS -Azure -Fonts
Configures workstation to base level dev configuration. Also adds fonts, python, aws, and azure utilities.
.PARAMETER Software
If specified installs additional software packages
.PARAMETER Python
If specified installs additional Python utilities and settings
.PARAMETER AWS
If specified installs additional AWS utilities and settings
.PARAMETER AWSCDK
If specified installs additional AWS CDK utilities for TypeScript CDK
.PARAMETER Azure
If specified installs additional Azure utilities and settings
.PARAMETER Fonts
If specified installs downloaded Nerd font packages
.NOTES
Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info
What does this actually do?
- Sets up PoshLog for logging
- winget is used as the primary package manager, so it validates that it is installed.
- Removes the old version of Pester that ships with Windows.
- It downloads a package config zip that contains:
- Various Nerd fonts
- Background photos for Windows Terminal
- Installs choco if required.
- Installs a base set of winget packages such as:
- git
- vscode insiders
- 7zip
- firefox
- oh-my-posh
- powershell
- Downloads configuration from GitHub gists and applies to proper locations:
- Configures VSCode with desired configurations
- Populates VSCode snippets
- Sets oh-my-posh theme configuration
- Configures Windows Terminal with desired configurations
- Sets PowerShell profile.ps1
- Installs base level desired PowerShell modules
- Install desired VSCode extensions
- Installs tech specific utilities/configs/extensions based on specified switches
#>
function Invoke-WSSetup {
[CmdletBinding()]
param (
[switch]$Software,
[switch]$Python,
[switch]$AWS,
[switch]$AWSCDK,
[switch]$Azure,
[switch]$Fonts
)
#Requires -Version 7
#Requires -RunAsAdministrator
#region logger configuration
# check if PoshLog module is available and install if not
if (-not (Get-Module -Name PoshLog -ListAvailable)) {
Write-Verbose 'PoshLog not found. Installing...'
Install-Module -Name PoshLog -Scope CurrentUser -Repository PSGallery -Force
# vaidate poshlog is installed and throw if not
if (-not (Get-Module -Name PoshLog -ListAvailable)) {
throw 'PoshLog not installed. Unable to continue.'
}
}
else {
Write-Verbose 'PoshLog found.'
}
# Create new logger
$timeSpan = New-TimeSpan -Seconds 60
New-Logger | # Create new instance of logger configuration
Set-MinimumLevel -Value Verbose -ToPreference |
Add-SinkFile -Path $loggerDirectory -RollingInterval Day -RetainedFileCountLimit 90 -FlushToDiskInterval $timeSpan |
# Add-SinkConsole | # Add sink that will log our event messages into console host
Start-Logger # Start logging
# Test all log levels
# Write-VerboseLog 'Test verbose message'
# Write-DebugLog 'Test debug message'
# Write-InfoLog 'Test info message'
# Write-WarningLog 'Test warning message'
# Write-ErrorLog 'Test error message'
# Write-FatalLog 'Test fatal message'
# Write-ErrorLog 'Error occurred while doing some business! {SomeNumber}' -ErrorRecord $_ -PropertyValues 123
# Write-ErrorLog 'Error occurred while doing some business!' -Exception $_.Exception
#endregion
$ProgressPreference = 'SilentlyContinue'
# verify if winget is installed
if (-not (Test-WinGet)) {
Write-ErrorLog 'WinGet not installed. You need to install App Installer from the Microsoft Store'
throw 'WinGet not installed. You need to install App Installer from the Microsoft Store'
}
Write-InfoLog 'Uninstalling old versions of Pester...'
Uninstall-Pester
# set-up the temp dir if required
Write-InfoLog 'Verifying temp dir...'
if (-not (Test-Path $script:tempPath)) {
Write-InfoLog ' CREATING temp dir.'
New-Item -Path $script:tempPath -ItemType Directory -Force
}
else {
Write-InfoLog ' VERIFIED Temp.'
}
Write-InfoLog 'make sure we have the workstation setup files...'
Get-WSSetupFiles
Write-InfoLog 'verify chocolatey is installed...'
if (-not (Test-Choco)) {
Install-Choco
}
Write-InfoLog 'installing base winget packages...'
foreach ($package in $script:wingetCoreTech) {
if (-not (Test-WingetInstall -WingetPackage $package)) {
Install-WingetPackage -WingetPackage $package
}
}
Write-InfoLog 'installing base choco packages...'
foreach ($package in $script:chocoCoreTech) {
if (-not (Test-ChocoInstall -ChocoPackage $package)) {
Install-ChocoPackage -ChocoPackage $package
}
}
Write-InfoLog 'Adding VSCode path to environment...'
$env:Path += ';C:\Program Files\Microsoft VS Code Insiders\bin'
$env:Path += ";$env:LOCALAPPDATA\Programs\Microsoft VS Code Insiders\bin"
Write-InfoLog 'Creating VSCode snippets path...'
if (-not (Test-Path $script:vscodeSnippetsPath)) {
New-Item -Path $script:vscodeSnippetsPath -ItemType Directory -Force
}
Write-InfoLog 'Installing base PowerShell modules...'
Install-BaseModules
Write-InfoLog 'Installing base VSCode extensions...'
Install-VSCodeExtension -ExtensionList $script:vscodeExtensions
Write-InfoLog 'Setting settings files...'
Set-SettingsFiles
Write-InfoLog 'Setting Windows Terminal background images...'
Set-BackImages
# installs fonts if font switch specified
if ($fonts) {
Write-InfoLog 'Installing fonts...'
Install-Fonts
} #if_fonts
# installs software packages if specified
if ($Software) {
Write-InfoLog 'Installing software packages...'
foreach ($package in $script:wingetSoftwareInstalls) {
if (-not (Test-WingetInstall -WingetPackage $package)) {
Install-WingetPackage -WingetPackage $package
}
}
foreach ($package in $script:chocoSoftwareInstalls) {
if (-not (Test-ChocoInstall -ChocoPackage $package)) {
Install-ChocoPackage -ChocoPackage $package
}
}
} #if_software
# installs azure resources if azure switch is specified
if ($azure) {
Write-InfoLog 'Installing Azure resources...'
# install azure winget packages
foreach ($package in $script:wingetInstallsAzure) {
if (-not (Test-WingetInstall -WingetPackage $package)) {
Install-WingetPackage -WingetPackage $package
}
}
# install azure choco packages
foreach ($package in $script:chocoInstallsAzure) {
if (-not (Test-ChocoInstall -ChocoPackage $package)) {
Install-ChocoPackage -ChocoPackage $package
}
}
# install azure Modules
Install-HelpfulAzureModules
# install azure extensions
Install-VSCodeExtension -ExtensionList $script:vscodeExtensionsAzure
# set azure path variables
Set-AzurePathVariables
} #if_azure
# installs aws resources if aws switch is specified
if ($aws) {
Write-InfoLog 'Installing AWS resources...'
# install aws winget packages
foreach ($package in $script:wingetInstallsAWS) {
if (-not (Test-WingetInstall -WingetPackage $package)) {
Install-WingetPackage -WingetPackage $package
}
}
# install aws choco packages
foreach ($package in $script:chocoInstallsAWS) {
if (-not (Test-ChocoInstall -ChocoPackage $package)) {
Install-ChocoPackage -ChocoPackage $package
}
}
# install aws tools installer module
Install-Module -Name AWS.Tools.Installer -Scope CurrentUser -Repository PSGallery -Force
# install aws modules
foreach ($module in $script:awsModules) {
Write-InfoLog (' {0} evaluating...' -f $module)
if (-not (Get-Module $module -ListAvailable)) {
Write-InfoLog (' Installing {0}' -f $module)
Install-AWSToolsModule -Name $module -Scope CurrentUser -Force
}
else {
Write-InfoLog (' {0} VERIFIED. NO ACTION TAKEN.' -f $module)
}
}
# special case for AWSLambdaPSCore
if (-not (Get-Module 'AWSLambdaPSCore' -ListAvailable)) {
Write-InfoLog (' Installing {0}' -f $module)
Install-Module -Name 'AWSLambdaPSCore' -Scope CurrentUser -Repository PSGallery -Force
}
else {
Write-InfoLog (' AWSLambdaPSCore VERIFIED. NO ACTION TAKEN.' -f $module)
}
# install aws extensions
Install-VSCodeExtension -ExtensionList $script:vscodeExtensionsAWS
} #if_aws
# installs aws cdk resources if awscdk switch is specified
if ($awscdk) {
Write-InfoLog 'Installing AWS CDK resources...'
# install aws winget packages
foreach ($package in $script:wingetAWSCDKTypeScript) {
if (-not (Test-WingetInstall -WingetPackage $package)) {
Install-WingetPackage -WingetPackage $package
}
}
Write-InfoLog 'Installing AWS CDK extensions...'
Install-VSCodeExtension -ExtensionList $script:vscodeExtensionsAWSCDK
Write-InfoLog 'Adding nodejs path to environment...'
$env:Path += ';C:\Program Files\nodejs'
Write-InfoLog 'Installing TypeScript compiler...'
npm install -g typescript
Write-InfoLog 'Installing AWS CDK library...'
npm install aws-cdk-lib
Write-InfoLog 'Installing AWS CDK...'
npm install -g aws-cdk
}
# installs python resources if python switch is specified
if ($python) {
Write-InfoLog 'Installing Python resources...'
# install python winget packages
foreach ($package in $script:wingetInstallsPython) {
if (-not (Test-WingetInstall -WingetPackage $package)) {
Install-WingetPackage -WingetPackage $package
}
}
Write-InfoLog 'Installing Python extensions...'
Install-VSCodeExtension -ExtensionList $script:vscodeExtensionsPython
} #if_python
Close-Logger
} #Invoke-WSSetup
#endregion
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment