Jumping to another section in a PowerShell script - powershell

I want to jump from one section in the script to another section in the same PowerShell script. My script is only going from top to bottom right now, but I have several tasks. e.g. I want to be able to choose from a task list in the beginning of the script and go to task number 2 skip task 1.
I hope this makes any sense for you. Take a look at my script:
Write-Host -ForegroundColor Yellow "welcome to my powershell script..."
""
""
""
Start-Sleep -Seconds 1
Write-Host -ForegroundColor Yellow "Choose a task:"
""
""
Write-Host -ForegroundColor Yellow "1. Clean up"
Write-Host -ForegroundColor Yellow "2. Uninstall Pre-Installed Apps"
Write-Host -ForegroundColor Yellow "3. Something should be written here"
""
""
""
While ($Valg -ne "1" -or $Valg -ne "2" -or $Valg -ne "3") {
$Valg = Read-Host "Choose a number from the task list"
If ($Valg –eq "1") { Break }
If ($Valg –eq "2") { Break }
If ($Valg –eq "3") { Break }
if ($Valg -ne "1" -or $Valg -ne "2" -or $Valg -ne "3") {
""
Write-Host -ForegroundColor Red "Ups. Try again..."
}
}
#### 1. First task should come here (clean up)
#some code here for the "cleaning up" task
#### 2. Second task here
#some code here for the "Uninstall Pre-Installed Apps" task
#### 3. Third task there
#Some code for the third task here
#### And so on...

Here is a generic solution which uses an array of PSCustomObject that represents the task (message and the function to invoke). It doesn't need a switch, you can simply add new Tasks to the array (and implement the desired function) without modifying the remaining script:
# define a task list with the messages to display and the functions to invoke:
$taskList = #(
[PSCustomObject]#{Message = 'Clean up'; Task = { Cleanup }}
[PSCustomObject]#{Message = 'Uninstall Pre-Installed Apps'; Task = { Uninstall }}
[PSCustomObject]#{Message = 'Something should be written here'; Task = { Print }}
)
# define the functions:
function Cleanup()
{
Write-Host "Cleanup"
}
function Uninstall()
{
Write-Host "Uninstall"
}
function Print()
{
Write-Host "Print"
}
# let the user pick a task:
Write-Host -ForegroundColor Yellow "Choose a task:"
$taskList | foreach -Begin { $i = 1;} -Process {
Write-Host -ForegroundColor Yellow ('{0}. {1}' -f ($i++), $_.Message)
}
do
{
$value = Read-Host 'Choose a number from the task list'
}
while($value -match '\D+' -or $value -le 0 -or $value -gt $taskList.Count)
# invoke the task:
& $taskList[$value-1].Task

I am updating answer to complete script based on inputs by "bluuf" and "majkinator".
Use the Switch-Case construct along with Functions like below. This is complete working solution.
#region Function Definitions. These come first before calling them
function FirstTask
(
[string] $inputVariable
)
{
"write any scipt for First task here without quotes. Input is: " + $inputVariable
}
function SecondTask
{
"write any scipt for Second task here without quotes"
}
function ThirdTask
{
"write any scipt for Third task here without quotes"
}
#endregion
#region Showing Options
Write-Host -ForegroundColor Yellow "welcome to my powershell script..."
""
""
""
Start-Sleep -Seconds 1
Write-Host -ForegroundColor Yellow "Choose a task:"
""
""
Write-Host -ForegroundColor Yellow "1. Clean up"
Write-Host -ForegroundColor Yellow "2. Uninstall Pre-Installed Apps"
Write-Host -ForegroundColor Yellow "3. Something should be written here"
Write-Host -ForegroundColor Yellow "0. Exit"
""
""
""
#endregion
#region Getting input
While ($true) {
$Valg = Read-Host "Choose a number from the task list"
If ($Valg –eq "0")
{
"Thanks for using my utility";
Break;
}
If (($Valg -ne "1") -and ($Valg -ne "2") -and ($Valg -ne "3") -and ($Valg -ne "0")) {
""
Write-Host -ForegroundColor Red "Ups. Try again..."
}
#region Main Code
switch ($Valg)
{
1{
FirstTask("sending input");
}
2 {
SecondTask;
}
3 {
ThirdTask;
}
default { "Please select a correct option."}
}
#endregion
}
#endregion

Related

selecting multiple options from the menu list

Set-ExecutionPolicy Bypass -Scope Process -Force;
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
}
# Step 2) define the array of packages you are offering
$Packages = 'googlechrome',
'firefox',
'codeblocks',
'windbg',
'nasm',
'explorersuite',
'pestudio',
'vscode',
'sysinternals',
'python',
'ccleaner',
'anaconda3',
'wireshark',
'sublimetext3',
'notepadplusplus',
'Exit'
# Step 3) define the Show-Menu function
function Show-Menu
{
Clear-Host
Write-Host "**********************************************"
Write-Host "LIST OF SOFTWARES"
# write the options using the array of packages
for ($i = 0; $i -lt $Packages.Count; $i++)
{
# {0,10} means right align with spaces to max 2 characters
Write-Host ('{0,10}. {1}' -f ($i + 1), $Packages[$i])
}
Write-Host " q. Exit the script"
Write-Host "*************************************************"
Write-Host
}
# Step 4) enter an endless loop you only exit if the user enters 'q'
while ($true)
{
Show-Menu
# $UserInput = Read-Host "Enter the software number to be installed"
$UserInput = Read-Host "Select the softwares number(s) to be installed"
$ok = $UserInput -match '[123456789101112131415]+$'
if( -not $ok)
{
write-host "Invalid selection"
sleep 2
write-host ""
}
until ($ok)
switch -Regex ($UserInput)
{
"1" {googlechrome}
"2" {firefox}
"3" {codeblocks}
"4" {windbg}
"5" {nasm}
"6" {explorersuite}
"7" {pestudio}
"8" {vscode}
"9" {sysinternals}
"10" {python}
"11" {ccleaner}
"12" {anaconda3}
"13" {wireshark}
"14" {sublimetext3}
"15" {notepadplusplus}
} until ($ok)
# test if the user wants to quit and if so, break the loop
if ($UserInput -eq 'q') { break }
# test if the user entered a number between 1 and the total number of packages (inclusive)
if ([int]::TryParse($UserInput,[ref]$null) -and 1..$Packages.Count -contains [int]$UserInput)
{
# here you install the chosen package using the array index number (= user input number minus 1)
$packageIndex = [int]$UserInput - 1
Write-Host "Installing $($Packages[$packageIndex])"
# Choco install $Packages[$packageIndex] -y
Choco install $Packages[$packageIndex] -y --ignore-checksums
}
else
{
$availableOptions = 1..$Packages.Count -join ','
Write-Host "Error in selection, choose $availableOptions or q" -Foreground Color Red
}
$null = Read-Host "Press Enter to continue"
}
I have written the script which is working when the user select the number then that corresponding software will be downloaded and installed. Now instead of selecting one option the user selects multiple options from the menu list then that software's will be downloaded and installed parallely so, I have modified my script for selecting multiple options but the script is not working so, please tell me how to achieve this functionality. Thanks in Advance
I've removed the parts which (for me) made no sense or produced errors in my environment - removed the option 'Exit' from $Packages and added the foreach part. So now the user can enter several numbers space separated so that $UserInput contains e.g. 1 2. this is then split an iterated in the foreach part.
Set-ExecutionPolicy Bypass -Scope Process -Force;
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
# Step 2) define the array of packages you are offering
$Packages = 'googlechrome',
'firefox',
'codeblocks',
'windbg',
'nasm',
'explorersuite',
'pestudio',
'vscode',
'sysinternals',
'python',
'ccleaner',
'anaconda3',
'wireshark',
'sublimetext3',
'notepadplusplus'
# Step 3) define the Show-Menu function
function Show-Menu
{
Clear-Host
Write-Host "**********************************************"
Write-Host "LIST OF SOFTWARES"
# write the options using the array of packages
for ($i = 0; $i -lt $Packages.Count; $i++)
{
# {0,10} means right align with spaces to max 2 characters
Write-Host ('{0,10}. {1}' -f ($i + 1), $Packages[$i])
}
Write-Host " q. Exit the script"
Write-Host "*************************************************"
Write-Host
}
# Step 4) enter an endless loop you only exit if the user enters 'q'
while ($true)
{
Show-Menu
$UserInput = Read-Host "Select the softwares number(s) to be installed (space separated)"
# test if the user wants to quit and if so, break the loop
if ($UserInput -eq 'q') { break }
foreach($input in $UserInput.Split(' ')) {
# test if the user entered a number between 1 and the total number of packages (inclusive)
if ([int]::TryParse($input,[ref]$null) -and 1..$Packages.Count -contains [int]$input)
{
# here you install the chosen package using the array index number (= user input number minus 1)
$packageIndex = [int]$input - 1
Write-Host "Installing $($Packages[$packageIndex])"
Choco install $Packages[$packageIndex] -y --ignore-checksums
} else {
$availableOptions = 1..$Packages.Count -join ','
Write-Host "Error in selection, choose $availableOptions or q" -Foreground Color Red
}
}
$null = Read-Host "Press Enter to continue"
}

Test-NetConnection being executed in switch when it should not

I have a powershell script that uses a menu and a selection. So loops until selection is 0. For one selection it tests entered port with "Test-NetConnection". But for some reason after this is ran, whenever another selection is made it seems "Test-NetConnection" is always ran, the banner shows up in powershell (even though it is very quick, but it shouldn't be executed.
I removed some selections. And I also have a few custom functions. But not sure why this is happening. Any help would be greatly appreciated!
Powershell...
while ($selection -ne 0) {
Write-Output ""
Write-Output "__MENU__"
Write-Output "0. Exit"
Write-Output "1. Set Target Machines"
Write-Output "2. Set Save Directory"
Write-Output "4. Save MSINFO32"
Write-Output "5. Save All Installed Software"
Write-Output "9. Save GPO Info (gpresult)
Write-Output "10. Test WMI"
Write-Output "11. Test TCP Port"
Write-Output "14. Test Syslog"
Write-Output ""
Write-Output "Target Machines: $($targetComputers -Join ',')"
Write-Output "Save Directory: $savePath`n"
$selection = Read-Host -Prompt "Selection"
Write-Output ""
switch ($selection) {
0 { break }
1 { ... }
2 { ... }
11 {
$testPort = Read-Host -Prompt "TCP Port to Test (Leave blank for default: 80)"
Write-Output ""
if (($testPort -eq $null) -Or ($testPort -eq "")) { $testPort = 80 }
foreach ($computer in $targetComputers) {
try {
$netcon = Test-NetConnection -ComputerName $computer -Port $testPort -ErrorAction "Stop"
if($netcon.TcpTestSucceeded) { Write-Output "TCP Port $testPort test was successful on $computer." }
else { Write-Warning "TCP Port $testPort test was unsuccessful on $computer." }
}
catch { Write-Warning "TCP Port $testPort test was unsuccessful on $computer." }
}
} #11
14 {
$sysServer = Read-Host -Prompt "Syslog Server (Leave blank for default: ${env:computername})"
$sysProto = Read-Host -Prompt "Syslog Server Protocol (Leave blank for default: TCP)"
$sysPort = Read-Host -Prompt "Syslog Server Port (Leave blank for default: 514)"
Write-Output ""
if (($sysServer -eq $null) -Or ($sysServer -eq "")) { $sysServer = $env:computername }
if (($sysProto -eq $null) -Or ($sysProto -eq "")) { $sysProto = "TCP" }
if (($sysPort -eq $null) -Or ($sysPort -eq "")) { $sysPort = 514 }
sendSyslog -Server $sysServer -Protocol $sysProto -Port $sysPort
} #14
default { break }
} #switch
} #while
Your menu option "9. Save GPO Info (gpresult) is missing a closing quote.
Make this easier for yourself and use a Here-String for the menu:
$menu = #"
__MENU__
0. Exit
1. Set Target Machines
2. Set Save Directory
4. Save MSINFO32
5. Save All Installed Software
9. Save GPO Info (gpresult)
10. Test WMI
11. Test TCP Port
14. Test Syslog
"#
Then instead of looping like while ($selection -ne 0), run an endless loop you can exit as soon as the user types a '0'.
# enter an endless loop here.
# exit that loop right after the Read-Host when the user type '0'
while ($true) {
Write-Host $menu
# some in-between code.. #
$selection = Read-Host -Prompt "Selection"
if ($selection -eq '0') { break } # or exit if you want to stop the entire script here
# rest of your code.. #
}
P.S. anything coming back from Read-Host is a string, not a number.

Do-Until loop ask twice value

The problem is with loop DoUntil.
The script must take the URL, than ask what to do with him and gave the result after the user pick the number(1-4)
and with output give the menu again, and user can pick another number if he want or finish the script typing 'X'.
But this script works in a different way, it is ask for URL, after I will pick the number, and after this it asks again the URL and again the number and after this(if for example I will pick twice the same number) it will bring me the value of 2 URLs together.
Please, advice, what I'm doing wrong.
write-host ""
write-host "Past URL that you need to check"
write-host ""
write-host -nonewline "Type your URL and press Enter: "
$URLok = read-host
write-host ""
if ($URLok.Length -lt 1) {
write-host "Invalid selection"
}
do {
write-host "What do you want to do with this URL?"
write-host ""
write-host "DNS - Selection 1"
write-host " MX - Selection 2"
write-host " NS - Selection 3"
write-host "TXT - Selection 4"
write-host ""
write-host "X - Exit"
write-host ""
write-host -nonewline "Type your choice and press Enter: "
$choice = read-host
$ok = $choice -match '^[1234x]+$'
if ( -not $ok) {
write-host "Invalid selection"
}
switch -Regex ( $choice ) {
"1"
{
[System.Net.Dns]::resolve($URLok)
}
"2"
{
Resolve-DnsName -Name $URLok -Type MX -DnsOnly | select Name,Type,NameExchange
}
"3"
{
Resolve-DnsName -Name '888ladies.com' -Type NS -DnsOnly | select Name,Type,NameHost,PrimaryServer
}
"4"
{
Resolve-DnsName -Name $URLok -Type TXT | select Name,Type,Strings
}
"5"
{
write-host ""
write-host "Past URL that you need to check"
write-host ""
write-host -nonewline "Type your URL and press Enter: "
$URLok = read-host
write-host ""
}
}
} while($choice -ne "X")
P.s. And Little remark, in the 2-3-4 options I have 'Resolve-DNSName' but this script wouldn't show it on the console before the loop doUntil finished. So I can see the result only if I pick 'X' and finish the loop
The answer was with the pip Format-Table and it's work!
For Example:
Resolve-DnsName -Name $URLok -Type NS -DnsOnly | select Name,Type,NameHost,PrimaryServer| Format-Table

Pause on failure, retry input from CSV

I have 2 sections of PowerShell code that exit the script on failure. After thinking about it I realized it would make more sense to pause on a failure and allow the admin time to remediate... then when the any key is paused, retry the same line in the CSV. Here's how it looks now:
#check tools status first
Write-Host ""
Write-Host "Checking VMware Tools Status before proceeding." -foreground green
Write-Host ""
foreach ($item in $vmlist) {
$vmname = $item.vmname
$ToolsStatus = (Get-VM $vmname).extensiondata.Guest.ToolsStatus -eq "toolsNotRunning"
if ($ToolsStatus -eq $true) {
Write-Host ""
Write-Host "Tools is not installed or running on $vmname. Remediate on guest and restart the script" -foreground Yellow
Write-Host "Script will continue to exit until tools is running on $vmname" -foreground yellow
Write-Host ""
exit
} else {
Write-Host ""
Write-Host "Tools running on all VMs, script will continue" -foreground green
Write-Host ""
}
}
I know how to put the pause in $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown'), but I have no idea to do the loop so it retries.
I'm using similar code elsewhere to validate that VMs are powered up so this will work in both sections.
EDIT: Here's the re-worked script. Would this work?
foreach ($item in $vmlist) {
$vmname = $item.vmname
do {
$ToolsStatus = (Get-VM $vmname).extensiondata.Guest.ToolsStatus -eq "toolsNotRunning"
if ($ToolsStatus) {
Write-Host ""
Write-Host "Tools is not installed or running on $vmname." -Foreground yellow
Write-Host "Remediate VMware tools on $vmname and"
Write-host "Press any key to retry..."
$Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
}
} until ($ToolsStatus -eq "PoweredOn")
Write-Host "Tools running on $vmname, script will continue" -Foreground green
}
Add a nested do..while loop like this:
foreach ($item in $vmlist) {
$vmname = $item.vmname
do {
$ToolsMissing = (Get-VM $vmname).extensiondata.Guest.ToolsStatus -eq "toolsNotRunning"
if ($ToolsMissing) {
Write-Host "Tools is not installed or ..." -Foreground yellow
$Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
}
} while ($ToolsMissing)
}
Write-Host "Tools running on all VMs, script will continue" -Foreground green

How do I allow for multiple selections in a numbered menu?

This is related to my previous question about FTP time stamps. I'm working on a script to help make my job, and hopefully others, better by making PC setups easier. As it is right now everything works perfectly, except I'd like to make it more automated. Right now it only allows for one thing at a time. I'd like to be able to choose multiple numbers and have it run them all one after the other.
This needs to work in Windows 7 without importing any extra modules.
Here's what I have so far:
echo ""
$answer = read-host "Select a number"
echo ""
if ($answer -eq 1){uninstall}
if ($answer -eq 2){flashjava}
if ($answer -eq 3){reader}
if ($answer -eq 4){firefox}
if ($answer -eq 5){chrome}
if ($answer -eq 6){office}
if ($answer -eq 7){reader}
if ($answer -eq 8){updates}
if ($answer -eq 9){exit}
else {write-host -ForegroundColor red "Invalid Selection"
sleep 2
mainmenu
}
echo ""
Instead of choosing option 2 (for example), letting that go, then hitting option 3, and so on I'd like to type "2,3" (no quotes) and have it hit both functions one after the other.
I've looked into foreach loops, but can't get it to work.
echo ""
$num = read-host "Select a number"
echo ""
$num.split(",")
ForEach($answer in $num){
if ($answer -eq 1){uninstall}
if ($answer -eq 2){flashjava}
if ($answer -eq 3){reader}
if ($answer -eq 4){firefox}
if ($answer -eq 5){chrome}
if ($answer -eq 6){office}
if ($answer -eq 7){reader}
if ($answer -eq 8){updates}
if ($answer -eq 9){exit}
}
else {write-host -ForegroundColor red "Invalid Selection"
sleep 2
mainmenu
}
echo ""
I'm aware the Else statement would catch anything other than a single 1-9. I've tried commenting that section out, still nothing. It works if I choose a single number, but not multiples. I've also tried putting the numbers in the if statements in quotes. It also displays the number(s) I've chosen before it either runs the function or quits. I'd like to not display any output.
Thanks!
UPDATE: After looking around I found the switch parameter. Seems to work great!
do {
do {
cls
write-host ""
write-host "****************************"
write-host "** Main Menu **"
write-host "****************************"
write-host ""
write-host "1 - Uninstall Bundled Apps"
write-host "2 - Install Flash & Java"
write-host "3 - Install Adobe Reader"
write-host "4 - Install Firefox"
write-host "5 - Install Chrome"
write-host "6 - Install Office 365"
write-host "7 - Install All"
write-host ""
write-host "8 - Check for Installer Updates"
write-host ""
write-host "9 - Exit"
write-host ""
$answer = read-host "Select number(s)"
$ok = $answer -match '[123456789]+$'
if ( -not $ok) {write-host "Invalid selection"
sleep 2
write-host ""
}
} until ($ok)
switch -Regex ( $answer ) {
"1" {uninstall}
"2" {flashjava}
"3" {reader}
"4" {firefox}
"5" {chrome}
"6" {o365}
"7" {uninstall}
"8" {updates}
}
} until ( $answer -match "9" )
}
You were really close actually. The main problem is that $num.Split() returns the array, but does not modify $num, so when you then use $num in your foreach() loop, you are using the original value. Have a look at the change here:
echo ""
$num = read-host "Select a number"
echo ""
$nums = $num.split(",")
ForEach($answer in $nums){
if ($answer -eq 1){uninstall}
if ($answer -eq 2){flashjava}
if ($answer -eq 3){reader}
if ($answer -eq 4){firefox}
if ($answer -eq 5){chrome}
if ($answer -eq 6){office}
if ($answer -eq 7){reader}
if ($answer -eq 8){updates}
if ($answer -eq 9){exit}
}
# else {write-host -ForegroundColor red "Invalid Selection"
# sleep 2
# mainmenu
# }
# commented out because this else no longer corresponds to anything
echo ""
Now just to clean this up a bit, let's look at it with switch:
echo ""
$num = read-host "Select a number"
echo ""
$nums = $num.split(",")
ForEach($answer in $nums){
switch ($answer) {
1 {uninstall}
2 {flashjava}
3 {reader}
4 {firefox}
5 {chrome}
6 {office}
7 {reader}
8 {updates}
9 {exit}
default {
write-host -ForegroundColor red "Invalid Selection"
sleep 2
mainmenu
}
}
}
echo ""