Note: Wix does not handle code boxes very well, please excuse any strange code formatting.
In the fall I was presented with a Remote Bios tool for HP, the challenge? We need WOL enabled on 80+ machines in the field and would rather not pay a technician or have to fly me out to perform Bios configurations. Sure, we could find value in sending me into the field, after all I am all things Contact Center... but if it can be remotely managed, it should be explored.
This tool uses the Windows Management Instrumentation (WMI) to communicate with supported Bios' via the executable and a few switches. A very clever tool.
Initially I thought "I can script this"
This was my original design...
#Variables
$ModelTest = (Get-WmiObject -Class:Win32_ComputerSystem).Model -replace " ", "_"
$BiosPath = "\\Network-Share\HPBios_Files\" + $ModelTest + ".txt"
$SourceExecutable = "\\Network-Share\HPBios_Files\BiosConfigUtility64.exe"
$ExecutableDestination = "C:\HPBios"
$Executable = "BiosConfigUtility64.exe"
Function InstallEXE{
#Checking for local Programs\HPBios Directory. Creating it if it doesn't exist
If (Test-Path $ExecutableDestination) {}
Else{
Write-Output "Creating HPBios Directory and Moving Executable"
New-Item -Path C:\Installs\Programs\ -Name "HPBios" -ItemType "directory"
}
#Checking for \HPBios Directory, if it exists, Copying Executable to it.
If (Test-Path $ExecutableDestination){
Copy-Item -Path $SourceExecutable -Destination $ExecutableDestination
}
}
Function ConfigureBIOS {
#Checking for Executable, if it exists, Applying HP Bios Config.
If (Test-Path "$ExecutableDestination\$Executable"){
Invoke-Expression "$ExecutableDestination\$Executable /setconfig:$BiosPath"
}
Else {
Write-Error "Executable Doesn't Exist"
}
}
If (Test-Path $BiosPath){
If (Test-Path "$ExecutableDestination\$Executable") {
ConfigureBIOS
}
Else {
InstallEXE
Start-Sleep 1
ConfigureBIOS
}
}
Else
{
Write-Error "Configuration File On The Server Doesn't Exist"
}
Looking back, it's very simple. But it looks dirty, like several reddit suggestions thrown together with no further direction. Hmm.
I showed it to my mentor, Devon, and as usual he explains the flaws in my logic and how I can improve on it. That's when he led me to Advanced Functions.
Essentially an advanced function allows a function to perform similarly to a cmdlet written in C#, with parameters to alter the functionality of your function.
The idea behind teaching me this was to help me clean up my code while giving me the tools to build upon later. What I learned was that it is bad practice to call on variables that are defined outside of the function they are being used in. If you are new to PowerShell or coding in general you maybe thinking "But your functions exist within the same scope as the defined variables... It'll still work" and you're right, It would work. But you could be shooting yourself in the foot later... essentially, if you have a large project with a bunch of functions it'll make debugging much easier for you if you can bring those variables into the function. But it's much cleaner to define all of your variables in the same place, that way you don't have random proprietary information scattered through out your script... it also makes writing reusable code much easier. How to we pull this off? Advanced Functions.
The following example is a basic advanced function.
function Set-AdvancedFunk {
param(
[string]$Funky
)
if($funky.toLower() -eq "we got the funk") {
Write-Host "We're gonna tear the roof off the mother, sucker."
}
else {
Write-Error "The Funk was not found"
}
}
Set-AdvancedFunk -Funky "We got the funk"
We're gonna tear the roof off the mother, sucker.
As you can see we were able to use the function Set-AdvancedFunk with the parameter -Funky passing "We got the funk". Because the if conditions were met, the output was PowerShell wanting to tear the roof off.
We can also use this to pass variables within a script, like so...
$theFunk = "We got the funk"
function Set-AdvancedFunk {
param(
[string]$Funky
)
if($funky.toLower() -eq "we got the funk") {
Write-Host "We're gonna tear the roof off the mother, sucker."
}
else {
Write-Error "The Funk was not found"
}
}
Set-AdvancedFunk -Funky $theFunk
We're gonna tear the roof off the mother, sucker.
Using this methodology I was able to clean up my code for the HP Bios Tool to this final implementation.
#Variables
$ModelTest = (Get-WmiObject -Class:Win32_ComputerSystem).Model -replace " ", "_"
#$BiosPath = "\\Network-Share\HPBios_Files\" + $ModelTest + ".txt"
$DefaultConfigsPath = "\\Network-Share\HPBios_Files\Default_Configs\" + $ModelTest + "_Default.txt"
$SourceExecutable = "\\Network-Share\HPBios_Files\BiosConfigUtility64.exe"
$ExecutableDestination = "C:\HPBios"
$Executable = "BiosConfigUtility64.exe"
Function InstallEXE {
param(
[Parameter(Mandatory=$True)][String]$ExecutableDestination,
[Parameter(Mandatory=$True)][String]$SourceExecutable,
[Parameter(Mandatory=$True)][String]$Executable
)
#Checking for Executable, Ignoring if it exists.
If(Test-Path "$ExecutableDestination\$Executable") {
return $True
}
#If Executable doesn't exist, checking for the HPBios Directory and Creates it without shell output.
If (-Not (Test-Path $ExecutableDestination)) {
Write-Output "Creating HPBios Directory and Moving Executable"
New-Item -Path $ExecutableDestination -ItemType Directory -Force |
Out-Null
}
#Checking that the Executable doesn't exist but that the HPBios Directory does, if true, Copying Executable to it.
If (-Not(Test-Path "$ExecutableDestination\$Executable") -And (Test-Path $ExecutableDestination)) {
Copy-Item -Path $SourceExecutable -Destination $ExecutableDestination
}
return (Test-Path "$ExecutableDestination\$Executable")
}
Function ConfigureBIOS {
param(
[Parameter(Mandatory=$True)][String]$ExecutableDestination,
[Parameter(Mandatory=$True)][String]$SourceExecutable,
[Parameter(Mandatory=$True)][String]$Executable,
[Parameter(Mandatory=$True)][String]$DefaultConfigsPath
)
#Runs the InstallExe Function and Checks for the Config file, If everything required exists, Applys the HP Config. Outputs Error otherwise.
If ((InstallExe -ExecutableDestination $ExecutableDestination -Executable $Executable -SourceExecutable $SourceExecutable)) {
Invoke-Expression "$ExecutableDestination\$Executable /getconfig:$DefaultConfigsPath"
}
Else{
Write-Error "Either $ExecutableDestination\$Executable is Missing"
}
}
ConfigureBIOS -ExecutableDestination $ExecutableDestination -Executable $Executable -SourceExecutable $SourceExecutable -DefaultConfigsPath $DefaultConfigsPath
Obviously I took the liberty to add some syntactic sugar as well.
Advanced Functions go much deeper than this simple application. If you are new to PowerShell, go to about_Functions_Advanced_Parameters and learn how to use Switches and Parameter Attributes to add flexibility to your functions.