I had a unique requirement to spin up a quick solution for the Tech Field Team to do various administrative tasks on clients at scale. Since database access was tricky, text files were used as tables and in each text file had client machine names. The Tech Field Team was able to control the text files themselves. I then had to develop scripts to loop through the text files for various tasks such as restarting services, installations, data collection, etc. Here were more requirements:

  • The text files must have a naming convention to be unique. Ex: 1-Prod, 2-QA, 3-Dev
  • The text files can have any amount of client machine names separated by new line
  • The script has an interactive menu which allows user to choose which text file to loop through. There is also validation in the menu so if user inputs an incorrect choice, it will validate.
  • If a client is offline, skip it
  • Log the script

Example Solution in PowerShell that can be used as a module:

########################### ONLY CHANGE THIS SECTION ###########################

$scriptTitle = “Restart Service Remotely”
$description = “This script will restart Service remotely.”
$wiki = “https://www.wiki.com”
$confirmationMessage = “Script completed! Please eSocket Service was restarted successfully.”
$logFolder = “restart-service-remotely”
$logName = “restart-service-remotely”

#Main Function to change
function doWork
{
param(
[String]$client
)

Write-Host “Restarting service on Client: $client”
Get-Service -ComputerName $client -Name service | Restart-Service -Force
}

########################### END OF SECTION TO CHANGE ###########################

$scriptName = $MyInvocation.MyCommand.Name
$todayDate = get-date -format “MM-dd-yyyy”
$host.ui.RawUI.WindowTitle = “$scriptTitle”
$startedScriptTime = Get-Date
$currentUser = whoami
$hostName = hostname

#logging
if (-not (Test-Path -Path ..\files\logs\$logFolder))
{
try
{
New-Item -Path ..\files\logs\$logFolder -ItemType Directory -ErrorAction Stop | Out-Null
}
catch
{
Write-Error -Message “Unable to create directory ‘..\files\logs\$logFolder’. Error was: $_” -ErrorAction Stop
}
}
Start-Transcript -Path ..\files\logs\$logFolder\$logName-$todayDate.txt -Append -IncludeInvocationHeader -Force

#Header script info
Write-Host “`n`n`n—————————————————————————-`n”
Write-Host “`nScript Name: $scriptName”
Write-Host “Script started on: $startedScriptTime”
Write-Host “Script ran by: $currentUser”
Write-Host “Script ran on machine: $hostname`n”
Write-Host “`n—————————————————————————-`n”

#Menu Loop
do
{
$dbFileName = “”

Write-Host -ForegroundColor Green “`n*************”
Write-Host -ForegroundColor Green “`nTABLE LIST`n”
Write-Host -ForegroundColor Green “*************`n”

#Print all db table txt files in database folder
$ToNatural= { [regex]::Replace($_, ‘\d+’,{$args[0].Value.Padleft(20)})}
Get-ChildItem -Path ..\database -Name | sort-object $ToNatural

Write-Host -ForegroundColor Green “`n*************”
Write-Host -ForegroundColor Green “`n$description`n”
Write-Host -ForegroundColor Green “Wiki: $wiki`n”
Write-Host -ForegroundColor Green “Step 1 – Enter an ID from table list”
Write-Host -ForegroundColor Green “Step 2 – Review client(s) and then confirm`n”
Write-Host -ForegroundColor Green “*************`n”

$dbID = Read-Host “Step 1 – Enter an ID from table list (ex: 9001, 46, 172, etc.)”
$dbFileName = Get-ChildItem -Path ..\database -Name -Filter “$dbID-*”

#If user presses Enter Key, inputs invalid ID such as space, or non-existent ID: Reloop menu and prompt that invalid
if (($dbFileName -eq $null) -or (!$dbID) )
{
Write-Host -ForegroundColor Red “`nInvalid ID. Try again…`n”
}
else
{
$clientList = (get-content -Path ..\database\$dbFileName )

Write-Host -ForegroundColor Yellow “`n`n`n`n`n`n`n`n`n`n`n`n*************”
Write-Host -ForegroundColor Yellow “`nClient(s) listed in $dbFileName `n”
Write-Host -ForegroundColor Yellow “*************`n”

foreach ($client in $clientList)
{
Write-Host $client
}

Write-Host -ForegroundColor Yellow “`n*************”
Write-Host -ForegroundColor Yellow “`n$description`n”
Write-Host -ForegroundColor Yellow “Wiki: $wiki`n”
Write-Host -ForegroundColor Yellow “Step 2 – Review client(s) and then confirm`n”
Write-Host -ForegroundColor Yellow “*************`n”

$confirmation_loop = $true
while ($confirmation_loop) {
$confirmation = Read-Host “Step 2 – Review client list. Do you want to continue for the listed client(s)? (y/n)”
if ($confirmation -eq ‘y’)
{
$offlineClients = @()
$onlineClients = @()
$runningScriptTime = Get-Date

Write-Host -ForegroundColor Green “`nScript started to do work on: $runningScriptTime`n”

foreach ($client in $clientList)
{
#Main if statement to call function
if (Test-Connection -ComputerName $client -Count 1 -quiet)
{
$onlinecClients += $client
doWork -client $client
}
else
{
$offlineClients += $client
Write-Host -ForegroundColor Red “Client: $client is unreachable, skipping.”
}
}

Write-Host “`n`n`n`n”

#Print out clients that were unreachable
if ($offlineClients.count -gt 0)
{
Write-Host -ForegroundColor Red “`nThe script was not able to reach the listed clients:`n”

foreach ($offlineClient in $offlineClients)
{
Write-Host $offlineClient
}
}

#Print out clients that were reachable
if ($onlineClients.count -gt 0)
{
Write-Host -ForegroundColor Green “`nThe script ran on the listed clients:`n”

foreach ($onlineClient in $onlineClients)
{
Write-Host $onlineClient
}
}

Write-Host -ForegroundColor Green “`n$confirmationMessage`n”
$confirmation_loop = $false
}
elseif ($confirmation -eq ‘n’)
{
$confirmation_loop = $false
Write-Host -ForegroundColor Red “`nExiting script. Please run script again.`n”
}
else
{
Write-Host -ForegroundColor Red “`nInvalid input. Try again…`n”
}
}
}
}
while ( ($dbFileName -eq $null) -or (!$dbID) )

#Ending script info
$endScriptTime = Get-Date
Write-Host -ForegroundColor Green “`nScript Name: $scriptName”
Write-Host -ForegroundColor Green “Script ended on: $endScriptTime”
Write-Host -ForegroundColor Green “Script ended by: $currentUser”
Write-Host -ForegroundColor Green “Script ended on machine: $hostname`n”

#Stop logging
Stop-Transcript

Written 05/18/2022