Skip to content

Instantly share code, notes, and snippets.

@arkag
Last active July 13, 2018 23:01
Show Gist options
  • Select an option

  • Save arkag/5d519a97c73c15ff65c4df3194f595d9 to your computer and use it in GitHub Desktop.

Select an option

Save arkag/5d519a97c73c15ff65c4df3194f595d9 to your computer and use it in GitHub Desktop.
Install some basic applications and configurations for all machines.
[CmdletBinding(DefaultParameterSetName = 'Set1')]
Param(
[Parameter(Mandatory, ParameterSetName = 'Set1')]
# Currently doesn't support 365 silent installers
[ValidateSet('2016OL'<#,'365IM','365MS'#>)]
[String]$Office,
[Parameter(Mandatory, ParameterSetName = 'Set2')]
[Switch]$NoOffice,
[Parameter(Mandatory)]
[String]$Name,
[Switch]$InstallDrivers
)
$drive = Get-CimInstance -ClassName Win32_Volume -Filter "Label='Capuchin'"
$driveLetter = $drive.Name
$admin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
if (!$admin) {
Write-Host "I need Administrative rights, please accept the UAC."
Start-Sleep -Seconds 1.5
foreach ($key in $PSBoundParameters.keys) {
$args += "-$key " + $PSBoundParameters["$key"]
}
Start-Process powershell.exe -Wait -Verb runAs -ArgumentList "$driveLetter\Bootstrap.ps1 $args"
Exit
}
function Write-Verbln {
[CmdletBinding()]
Param ([String]$Sentence)
Write-Verbose -Message $Sentence
Write-Verbose -Message "---------`n"
}
$tmp = "C:\tmp"
Write-Verbln "Drive detected as: $driveLetter"
$computer = Get-CimInstance Win32_ComputerSystem
$computerModel = $computer.Model
Write-Verbln "Computer model detected as: $computerModel"
if (!$NoOffice) {
$officeExecutable = "$tmp\$Office\setup.exe"
$officeConfig = "$tmp\$Office\configuration.xml"
}
Write-Host "Bootstrapping..."
Start-Transcript -Path "$driveLetter\logs\$Name-Log.txt"
Write-Verbln "Copying data to $tmp."
Copy-Item -Path $driveLetter\bin -Destination $tmp -Force -Recurse
Write-Verbln "$($driveLetter)bin copied to $tmp."
if ($InstallDrivers -and (Test-Path $tmp\$computerModel)) {
$drivers = Get-ChildItem $tmp\$computerModel
foreach ($d in $drivers.Name) {
Write-Verbose -Message "Installing $d Driver"
Start-Process -FilePath $tmp\$computerModel\$d -ArgumentList /s -Wait
}
Write-Verbln "`tDone"
}
else {
Write-Error "The drivers for your $computerModel are not in $tmp, please download them and place them under a folder named `"$ComputerModel`" before passing the -InstallDrivers flag."
Start-Sleep -Seconds 1
}
$files = Get-ChildItem $tmp
foreach ($n in $files.Name) {
Write-Verbose "Installing $n..."
switch -Wildcard ($n) {
"*.msi" {Start-Process -Wait -FilePath "$tmp\$n" -ArgumentList /quiet}
"AGENT*" {Start-Process -Wait -FilePath "$tmp\$n" -ArgumentList /VERYSILENT}
"Firefox*" {
Write-Verbose -Message "Downloading Newest Installer"
Invoke-WebRequest -Uri "https://download.mozilla.org/?product=firefox-latest-ssl&os=win64&lang=en-US" -OutFile "$tmp\bin\FirefoxInstaller.exe"
Write-Verbose -Message "Copying Newest installer to flashdrive"
Copy-Item -Path "$tmp\bin\FirefoxInstaller.exe" -Destination $driveLetter\bin -Force -Recurse
Start-Process -Wait -FilePath "$tmp\$n" -ArgumentList -ms
}
"Systems-*" {Start-Process -Wait -FilePath "$tmp\$n" -ArgumentList /s}
$Office {Start-Process -FilePath $officeExecutable -ArgumentList "/config $officeConfig" -Wait}
}
Write-Verbln "`tDone"
}
Rename-Computer -NewName $Name
Write-Host "I've finished initial setup for this machine."
Write-Host "Restarting in 5 seconds..."
Start-Sleep -Seconds 5
Restart-Computer
@vexx32
Copy link
Copy Markdown

vexx32 commented Jul 12, 2018

if (!$admin) {
    Write-Host  "I need Administrative rights for the automated jujitsu I'm about to perform...`n"
    Read-Host   "Please press ENTER and then confirm the UAC prompt"
    Start-Process powershell.exe -Wait -Verb runAs -ArgumentList "-NoProfile -ExecutionPolicy Bypass $($drive.Name)GGT-Bootstrap.ps1"
    Exit
}

Read-Host probably isn't the best choice. Best to just use pause if you need that.

function Write-Hostln {
    Param ($Sentence)

    Write-Host $Sentence
    Start-Sleep -Milliseconds 100
    Write-Host "---------`n"
    Start-Sleep -Milliseconds 50
}

Probably want to apply [CmdletBinding()] and type it as a string parameter.

function New-BackspaceLn (){
    Param ($Sentence)

    $Backspaces = "`b" * $Sentence.Length
    return "$Sentence$Backspaces"
}

Either function{param()} or function(){}, not both. Pretty sure PS would just complain about it, but best to remove it either way.

function Write-HostSimType {
    # Revise parameter names. Suggest: InputString, ???
    # If the next person coming along can't figure out what your parameters are for, you need to rethink their names
    # Especially with switches; should have obvious names, always, like functions.
    Param($TypeMe, [switch]$KeepMe) # Type that first param as string

    if ($KeepMe){$TypeMe = New-BackspaceLn $TypeMe}
    $charr = $TypeMe.ToCharArray()
    foreach ($l in $charr){ # Rethink variable names. Suggest ($Char in $CharArray)
        $delay = 25
        if (@(",",".","?","!") -Contains $l){
            $delay = 300
        } elseif ($l -Match "`b" ){ # -eq should be fine here, might need a [char] cast but yeah
            $l = "`b `b"
        }
        Write-Host -NoNewLine $l
        Start-Sleep -Milliseconds $delay
    }
}
Copy-Item -Path $driveLetter\bin -Destination $tmp -Force -Recurse -Verbose | Write-Thinking $_

If this works, you're lucky. $_ isn't valid in this context, so it's probably being read as $null (uninitialised). Shouldn't need it there.

switch -Wildcard ($computerModel) {
    "Latitude E*" {$computerName = "$($computerModel.Substring(0, 3).ToUpper())E$($computerModel.Substring($computerModel.length-4, 4))-"}
    "Latitude*" {$computerName = "$($computerModel.Substring(0, 3).ToUpper())$($computerModel.Substring($computerModel.length-4, 4))-"}
}

# This ^^^ is very messy. Suggest:

if ($computerModel -like "Latitude E*") {
    $E = 'E$'
}
$computerName = '{0}{1}{2}-' -f @(
    $computerModel.Substring(0, 3).ToUpper()
    $E
    $computerModel.Substring($computerModel.length-4, 4)
)

You also have a bunch of Read-Host prompts, which... should generally be avoided. Most, if not all of these can be script parameters, and should be. The things you want user confirmation on should implement $PSCmdlet.ShouldProcess() or .ShouldContinue() as appropriate for proper y/n/a/na prompts. Multiple choice selects can be done with Out-GridView -OutputMode [Single|Multiple] much more effectively and with less hassle.

The installer executions could be pulled into a separate function to facilitate easier error handling and more sophisticated handling of the installations -- or you could look into using Chocolatey.

As a general rule, a good script should have minimal user interaction, and provide parameters such that if you want to run it with none it works flawlessly. It also should also use Write-Verbose instead of Write-Host in most places, if not all, and implement [CmdletBinding()] in order to make proper use of it. Use of [CmdletBinding(SupportsShouldProcess)] can be used to enable $PSCmdlet.ShouldProcess() as mentioned above.

It's also a good idea to get in the habit of building useful output objects and outputting most if not all of your data with them. This allows you to capture and make use of the output. For example, if you kept track of: applications installed, computer name changes, errors encountered, you could capture the script output and filter for (for example) computers that the script failed to fix in a neat little table that shows you exactly what failed for which machines, without scrolling through what would probably end up being hundreds of pages of not strictly necessary write-host babble if you had to run the script against a bunch of machines.

Speaking of, implement proper pipeline support. Nothing better than a Get-Content ComputerList.txt | Do-AllTheThings and sit back and sip coffee/tea/water for a good half hour while you go through every computer on the network effortlessly.

That's one of the reasons Write-Host is so discouraged by a lot of people (myself included) -- you literally cannot suppress it without editing the script. Use Write-Verbose or Write-Information instead. Sure, they hide their streams by default, but it's easier to tack on a -Verbose switch than it is to rewrite an entire script because it keeps badgering you with Write-Host.

You've also got a lot of Start-Sleep in here. I get it, it looks nicer. But when you get down to brass tacks, it's going to add a lot of runtime to the script if you're running it more than once or twice, or iterating over pipeline input.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment