Powershell - Loop script until user chooses to exit - powershell

How can I start a script over again? I have 3 switches and I want them to revert back to the beginning of the script.
Import-Module ActiveDirectory
Write-Host "--Please Login using a.account--"
#login
$credential = Get-Credential
#Main
Write-Host "--Remote Computer Rename v2.0--"
Write-Host "1. Query AD (Outputs to a text file)"
Write-Host "2. Quick computer rename"
Write-host "3. Quit"
$choice=Read-Host "Chose a number to continue"
#AD Query for computer
switch ($choice)
{
1 {
Write-Host "--Enter first five characters of computer name or full computer name i.e. USCLT--"
$cn=Read-Host 'Computer name'
$out="$cn*"
Get-ADComputer -Filter 'SamAccountName -like $out' >> c:\myscripts\dsquery.txt
Write-Host "Query complete. See dsquery.txt saved to Desktop."
}
...rest of my code.
So after See dsquery.txt saved to Desktop." I want it to go back to write-host portion.

Simple, short, stupid:
& cmd /c pause
exit
This will even contribute the "Press any key" message the TO requested. If you prefer to stay in PowerShell:
Read-Host "Press any key to exit..."
exit
But we may also get the input back:
$reply = Read-Host "Please type EXIT to exit"
if ($reply -eq "EXIT") { exit; }
I like that Read-Host exits the script when typing Ctrl-C, like cmd's pause does.

My personal favorite for checking user input is the do { } until () loop. Here is your code with the added loop, this will accomplish what your looking for:
Import-Module ActiveDirectory
Write-Host "--Please Login using a.account--"
#login
$credential = Get-Credential
#Main
do {
Write-Host "--Remote Computer Rename v2.0--"
Write-Host "1. Query AD (Outputs to a text file)"
Write-Host "2. Quick computer rename"
Write-host "3. Quit"
$choice=Read-Host "Chose a number to continue"
#AD Query for computer
switch ($choice)
{
1 {
Write-Host "--Enter first five characters of computer name or full computer name i.e. USCLT--"
$cn=Read-Host 'Computer name'
$out="$cn*"
Get-ADComputer -Filter 'SamAccountName -like $out' >> c:\myscripts\dsquery.txt
Write-Host "Query complete. See dsquery.txt saved to Desktop."
}
...rest of my code.
} until ($choice -eq 3)
This is a pretty unique strategy in my opinion. I took this from Jerry Lee Ford’s book : Microsoft Windows PowerShell Programming for the absolute beginner
you can read more about these and every other loop in powershell here : http://www.powershellpro.com/powershell-tutorial-introduction/logic-using-loops/

From a blog post I found:
echo "Press any key to exit..."
$Host.UI.RawUI.ReadKey("NoEcho, IncludeKeyDown") | OUT-NULL
$Host.UI.RawUI.FlushInputbuffer()

Enclose the whole thing in a while (1) {} block. That creates an infinite loop that will only terminate if it encounters a break (end the loop) or exit (end the script). Presumably, option 3 will lead to one of those statements.

Related

Powershell Script to compare File-Hash from a Stream and published

Good morning guys,
I'm new to powershell scripting. And i can't figure out what I'm doing wrong.
I tried to write a .ps1 script to compare the hash value of a stream. I used the microsoft documentation for help and modify it to a runable script so i don't need to write it over and over again.
$wc = [System.Net.WebClient]::new()
$pkgurl = Read-Host "Please enter Package Url: "
$publishedHash = Read-Host "Enter Published Hash: "
$FileHash = Get-FileHash -InputStream ($wc.OpenRead($pkgurl))
if ($FileHash.Hash -eq $publishedHash) {
Write-Host "File Hash is equal to published Hash."
}
else {
Write-Host "File Hash NOT equal to published Hash."
}
When i run the script and enter the package url and the published Hash, the program all of a sudden abruptly shuts down.
Please, anyone an idea?
The script ends as it has nothing else to do.
You can add read-host at the end to wait for user input before it closes. (it wont do anything with the input, this just forces it to stay open until input has been made.)
Alternatively if you want to use it multiple times without it closing you can create a loop:
$KeepOpen = $true
While($KeepOpen -eq $true){
$wc = [System.Net.WebClient]::new()
$pkgurl = Read-Host "Please enter Package Url: "
$publishedHash = Read-Host "Enter Published Hash: "
$FileHash = Get-FileHash -InputStream ($wc.OpenRead($pkgurl))
if ($FileHash.Hash -eq $publishedHash) {
Write-Host "File Hash is equal to published Hash."
}
else {
Write-Host "File Hash NOT equal to published Hash."
}
$user_input = Read-Host "Please enter Y to run again"
if($user_input -ne "Y"){
$KeepOpen = $false
}
}
This will keep the script open so you can see the results and if you want it to run again insert Y and hit enter and you should be back to where you start.

Change currently running script

Is there any way to add text to specific part of script to the currently running script?
If i have a menu with options:
Install All
Add item
Quit
Could the Add item be possible?
Learning to use powershell (heavy user of batches).
When entering Add item, then a read-host would pop up, adding a row between the long row of ### addwifi -wnm $USERINPUT afterwards 'restarting' the script.
Current script:
#cmd: title Add****
$host.ui.RawUI.WindowTitle = "Add Wi-Fi networks"
#When Show-Menu –Title 'SetupWi-Fi' is called
function Show-Menu
{
# NOTE if changing warible from somewhere else (Show-Menu -WARIBLE VALUE) then param part must be included
param (
[string]$Title = 'SetupWi-Fi'
)
Clear-Host
#cls and echo on #echo off
Write-Host "================ $Title ================"
Write-Host "a: Add Wi-Fi networks."
Write-Host "q: Quit."
}
#Do this until x
#For future shortening purposes
function addwifi
{
param (
[string]$wnm
#wnm= wifi name
)
netsh wlan add profile filename="$wnm.xml"
#for some reason (nice for this script) . stops the warible name
}
do
{
# call Show-Menu and optionally change varible: –Title 'Warible' changes the $title varible
Show-Menu
# makin varible chaase equal user input, placing Selection before it
$chaase = Read-Host "Selection:"
#switch according to the varible chaase
switch ($chaase)
{
'a' {
#'single quote' acts as echo, now executing commands of 'a' varible
'Adding Wi-Fi networks.'
$host.ui.RawUI.WindowTitle = "Adding Wi-Fi networks"
#note the upper function is called with warible
#add below here! #####################################################################
addwifi -wnm laptopidee
#add above here! #####################################################################
}
#close a execution
#close switch
}
#close do
}
#until x: selection == input q
until ($chaase -eq 'q')
One possibility is to use placeholders that you replace at runtime, though I'm not sure how well it will hold up for more complex scripts.
For example, if you have the following script:
$scriptPath = "$PsScriptRoot\$($MyInvocation.MyCommand.Name)"
$scriptContent = Get-Content "$PsScriptRoot\$($MyInvocation.MyCommand.Name)" -Raw
$newItem = Read-Host "Please enter new command"
##Placeholder
$scriptContent -replace "$([char]0x0023)$([char]0x0023)Placeholder", "$([char]0x0023)$([char]0x0023)Placeholder$([char]0x000D)$([char]0x000A)$newItem" |
Set-Content -Path $scriptPath
Each time you run it, you will be prompted for a new command, which will be added below the ##Placeholder. So, if you enter Get-Process when prompted, the script would end up on-disk like this:
$scriptPath = "$PsScriptRoot\$($MyInvocation.MyCommand.Name)"
$scriptContent = Get-Content "$PsScriptRoot\$($MyInvocation.MyCommand.Name)" -Raw
$newItem = Read-Host "Please enter new command"
##Placeholder
Get-Process
$scriptContent -replace "$([char]0x0023)$([char]0x0023)Placeholder", "$([char]0x0023)$([char]0x0023)Placeholder$([char]0x000D)$([char]0x000A)$newItem" |
Set-Content -Path $scriptPath
Next time you run the script you will be prompted for a new command, which is added to the list, and all commands already on the list will be executed.
Yes. Use external files as sources to be pulled in. The Add Item menu option creates another file to be read in at next execution.
Many people did this with batch files using .ini files to hold parameters. Similar construct.

How to configure a timeout for Read-Host in PowerShell

Like I said, this code works in PowerShell version 2, but not in PowerShell version 5.
function wait
{
$compte = 0
Write-Host "To continue installation and ignore configuration warnings type [y], type any key to abort"
While(-not $Host.UI.RawUI.KeyAvailable -and ($compte -le 20))
{
$compte++
Start-Sleep -s 1
}
if ($compte -ge 20)
{
Write-Host "Installation aborted..."
break
}
else
{
$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyup")
}
if ($key.character -eq "y")
{Write-Host "Ignoring configuration warnings..."}
else
{Write-Host "Installation aborted..."
}}
The official documentation or Read-Host -? will tell that it's not possible to use Read-Host in that manner. There is no possible parameter to tell it to run with some kind of timeout.
But there are various other questions detailing how to do this in PowerShell (usually utilizing C#).
The idea seems to be to check whenever the user pressed a key using $Host.UI.RawUI.KeyAvailable and check that for the duration of your timeout.
A simple working example could be the following:
$secondsRunning = 0;
Write-Output "Press any key to abort the following wait time."
while( (-not $Host.UI.RawUI.KeyAvailable) -and ($secondsRunning -lt 5) ){
Write-Host ("Waiting for: " + (5-$secondsRunning))
Start-Sleep -Seconds 1
$secondsRunning++
}
You could use $host.UI.RawUI.ReadKey to get the key that was pressed. This solution probably would not be acceptable if you need more complex input than a simple button press. See also:
Windows PowerShell Tip of the Week - Pausing a Script Until the User Presses a Key
PowerTip: Use PowerShell to Wait for a Key Press (Hey, Scripting Guy!)
Seth, thank you for your solution. I expanded on the example you provided and wanted to give that back to the community.
The use case is a bit different here - I have a loop checking if an array of VMs can be migrated and if there are any failures to that check the operator can either remediate those until the checks clear or they can opt to "GO" and have those failing VMs excluded from the operation. If something other than GO is typed state remains within the loop.
One downside to this is if the operator inadvertently presses a key the script will be blocked by Read-Host and may not be immediately noticed. If that's a problem for anyone I'm sure they can hack around that ;-)
Write-Host "Verifying all VMs have RelocateVM_Task enabled..."
Do {
$vms_pivoting = $ph_vms | Where-Object{'RelocateVM_Task' -in $_.ExtensionData.DisabledMethod}
if ($vms_pivoting){
Write-Host -ForegroundColor:Red ("Some VMs in phase have method RelocateVM_Task disabled.")
$vms_pivoting | Select-Object Name, PowerState | Format-Table -AutoSize
Write-Host -ForegroundColor:Yellow "Waiting until this is resolved -or- type GO to continue without these VMs:" -NoNewline
$secs = 0
While ((-not $Host.UI.RawUI.KeyAvailable) -and ($secs -lt 15)){
Start-Sleep -Seconds 1
$secs++
}
if ($Host.UI.RawUI.KeyAvailable){
$input = Read-Host
Write-Host ""
if ($input -eq 'GO'){
Write-Host -ForegroundColor:Yellow "NOTICE: User prompted to continue migration without the blocked VM(s)"
Write-Host -ForegroundColor:Yellow "Removing the following VMs from the migration list"
$ph_vms = $ph_vms | ?{$_ -notin $vms_pivoting} | Sort-Object -Property Name
}
}
} else {
Write-Host -ForegroundColor:Green "Verified all VMs have RelocateVM_Task method enabled."
}
} Until(($vms_pivoting).Count -eq 0)
Also note that all this $Host.UI stuff doesn't work from the Powershell ISE.
To find out from within a script you could test for $Host.Name -eq "ConsoleHost". When true you can use the code from this topic. Otherwise you could use $Host.UI.PromptForChoice or any other way of showing a dialog box. With System.Windows.Forms.Timer you can then set a timer, and code to close the dialog box or form can be run when it expires.

powershell Import-csv pop up

so i am working with a bit of code that after setting a static location for the CSV i am using for import. When i run the code it seems to run until i get the Windows can't open this File pop. you know the one with what do you want to do options, like user the web services to find the correct program. i am copying the code so hopefully someone can point on where i made this fubar. just for note before i made the CSV static the Script asked you to type the location in every time so maybe i missed a setting there
if ($args[0] -eq $null)
{
$userNameFile = D:\lync_creation\userlog.csv
$userNameFile = $usernamefile -replace '"',""}
else
{$usernamefile = $args[0]}
if ($userNameFile -ne "")
{$csv=import-csv $userNameFile}
else
{"Could not find a valid .csv with the user information."
exit}
foreach($c in $csv)
# enable for lync
{
"Enabling " + $c.Identity + " for Lync 2010"
Enable-csuser -identity $c.Identity -registrarpool pool01.west.com –sipaddresstype EmailAddress
}
write-host "Press any key to continue..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown,AllowCtrlC")
You are seriously over complicating things if you're just going to use a static location for your CSV file.
$csv = Import-CSV D:\lync_creation\userlog.csv
foreach($c in $csv){
"Enabling $($c.Identity) for Lync 2010"
Enable-csuser -identity $c.Identity -registrarpool pool01.west.com –sipaddresstype EmailAddress
}
write-host "Press any key to continue..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown,AllowCtrlC")
The first line imports the CSV file into a variable.
The next 4 loops through all entries in that variable, write the host who it's enabling and then enables the person.
The last 2 lines give a Press any key to continue message and then waits for a key press before continuing.

Windows PowerShell 2.0 for the absolute beginner - errors on the last project in the book

Working through Jerry Lee Ford Jr.'s Powershell 2.0 for the Absolute Beginner book. There's a lot of typographical errors in the code of the book that I've been able to work through and correct (great practice), but there's one I simply can't seem to figure out. Downloaded his stock code from the companion website, and his code is throwing the exact same error. Code is as follows:
# *************************************************************************
#
# Script Name: GameConsole.ps1 (The PowerShell Game Console)
# Version: 1.0
# Author: Jerry Lee Ford, Jr.
# Date: January 1, 2007
#
# Description: This PowerShell script provides a listing of PowerShell
# game scripts and allows the player to play any game by
# entering its menu number.
#
# *************************************************************************
# Initialization Section
$menuList = #() #Stores an array containing information about script games
$playAgain = "True" #Controls the execution of a loop that controls game
#execution
# Functions and Filters Section
#This function gets the player's permission to begin the game
function Get-GameListing {
$gameList = #() #Stores and array containing a list of PowerShell scripts
$i = 0 #Used to set the index value of the array when adding elements to it
Clear-Host #Clear the screen
Write-Host #Display a game console header
Write-Host " --------------------------------------------------------------"
Write-Host " Windows PowerShell Game Console" -foregroundColor darkred
Write-Host " --------------------------------------------------------------"
$location = Set-Location C:\ShellScripts\Games #Specify the location of the game scripts
#Load an array with a list of all the PowerShell scripts in the specified folder
$gameList = Get-ChildItem . *.ps1 # | ForEach-Object -process {$i++; $gameList[$i] = $_.Name }
$gameList #Return the contents of the array to the calling statement
}
#This function displays a menu listing of PowerShell games
function Write-MenuList {
param($list) #The list of games to be displayed is passed as an array
$Counter = 0 #Used to number each menu item
Write-Host ""
ForEach ($i in $list) { #Iterate for each script stored in the array
$counter++ #Increment the counter by 1
if ($counter -lt 10) { #Format the display of the first 9 scripts
Write-Host " $counter. $i" -foregroundColor blue
}
else { #Format the display of all remaining scripts
Write-Host " $counter. $i" -foregroundColor blue
}
}
Write-Host "`n --------------------------------------------------------------"
}
function End-ScriptExecution {
Clear-Host #Clear the screen
Write-Host "`n Thank you for using the Windows PowerShell Game Console"
Start-Sleep 3 #Pause the execution of the script for 3 seconds
Clear-Host #Clear the screen
}
# Main Processing Section
$response = 0 #Stores player input
#Continue playing new games until the player decides to close the game console
while ($playAgain -eq "True") {
#Call the function that generates an array containing a list of game scripts
$menuList = Get-GameListing
#Call function that converts the contents of the array into a list of menu items
Write-MenuList $menuList
#Prompt the player to pick a game to play
$response = Read-Host "`n Enter the menu number for a game or Q to quit"
#Prepare to close the game console when the user decides to quit
if ($response -eq "Q") {
$playAgain = "False" #Modify variable value in order to terminate the loop
continue #Repeat the loop
}
#Convert the player's input to a integer and then validate the player's input
if ([int]$response -lt 1) { #Anything below 1 is not a valid menu number
Clear-Host #Clear the screen
Write-Host "`n `a`aInvalid selection."
Read-Host #Pause the script until the player presses the Enter key
continue #Repeat the loop
}
if ([int]$response -gt $menuList.length) {
Clear-Host #Clear the screen
Write-Host "`n `a`aInvalid selection."
Read-Host #Pause the script until the player presses the Enter key
continue #Repeat the loop
}
Invoke-Expression $menuList[$response] #Executed the selected game script
Clear-Host #Clear the screen
}
End-ScriptExecution
The error being thrown is:
The term 'fortuneteller.ps1' is not recognized as the name of a cmdlet, functio
n, script file, or operable program. Check the spelling of the name, or if a pa
th was included, verify that the path is correct and try again.
At line:1 char:18
+ fortuneteller.ps1 <<<<
+ CategoryInfo : ObjectNotFound: (fortuneteller.ps1:String) [], C
ommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
No matter what ps script menu item I choose, I'm getting the same error. Any help would be greatly appreciated!
Modify this line:
Invoke-Expression $menuList[$response] #Executed the selected game script
to:
Invoke-Expression $menuList[$response].FullName
or my preference:
& $menuList[$response].FullName
It was failing most likely because it is trying to execute "FortuneTeller.ps1" and the working directory is either not in that dir or even if it is in that dir, PowerShell will not execute a script from the current dir without specifying the path e.g. ".\FortuneTeller.ps1". By specifying the full path via the FullName property this problem is avoided.