Requiring users to populate field (Powershell) - powershell

I have a snippet in powershell that reads questions from a flat file and prompts the user to provide a boolean or string response (i.e. Do you like flying cars? Why do you like skydiving?). All answers are written to a .xls for consumption to a DB later.
I can make the script repeat question for users that do not select a boolean answer (i.e. A, B, C or "Yes", "No"). However getting a user to provide a string (short) answer) is a bit more trickier.
$Question7 = Get-Content -path $PSScriptRoot\src\Question7.txt -raw
Write-Host $Question7 -ForegroundColor Yellow
$reason_for_hobby = Read-Host -Prompt "Please write in the answer"
Writ-Host "Answer: $reason_for_hobby" -ForegroundColor Green
Add-Member -inputObject $infoObject -memberType NoteProperty -name "HBYREASON" -
value $reason_for_hobby
I am trying to figure out how to force users to provide at least a 215 character response, and to repeat the question if one is not provided.
Thanks and stay safe.

You can achieve this with a simple while-loop (While $reason_for_hobby has less than 215 characters repeate):
while ($reason_for_hobby.Length -lt 215){
$reason_for_hobby = Read-Host -Prompt "Please write in the answer"
}
However better would be a do-while-loop if the variable $reason_for_hobby is not initialized before (do this once and repeate while $reason_for_hobby has less than 215 characters):
do{
$reason_for_hobby = Read-Host -Prompt "Please write in the answer"
}while ($reason_for_hobby.Length -lt 215)

Related

How to Append Text to File (with Powershell)

I am new to PowerShell, and am having trouble getting it to "echo" some variables back to a generated '.ps1' file. The use case is that I am attempting to make a script generator for media conversion that will generate another re-useable script once you enter all the required information.
Here is a sample of the code:
# Initialize
$TRACK_COUNTER=0
$artist=Read-Host -Prompt "Artist/Band Name"
$language=Read-Host -Prompt "Language (use ISO 639-2 codes)"
$album_or_single=Read-Host -Prompt "Is this an Album/Sound-Track or a collection of Singles (0 for album/sound-track, 1 for single's)"
# Album
if ($album_or_single -eq 0) {
$album=Read-Host -Prompt "Name of the Album"
$date=Read-Host -Prompt "Enter the release date of the album (in YYYY-MM-DD format, or type nothing if unknown)"
$genre=Read-Host -Prompt "Genre of the Album"
$cover_art=Read-Host -Prompt "Select the audio/image file with the Cover Art (ex. MP3/M4A or JPG/PNG)"
Get-ChildItem "*.mp3"
$track_amount=Read-Host -Prompt "How many Tracks/Songs are in this Album"
New-Item "$album [Generated].ps1" -ItemType File
Write-Output -Path "$album [Generated].ps1" -Value 'This is a text test.'
}
# Singles
if ($album_or_single -eq 1) {
$track_amount=Read-Host -Prompt "How many seperate Single's are there"
}
Sorry for the bad formatting, and the (probably) improper use of brackets. The problem is that nothing ends up going into the generated file (it actually ends up generating). The last thing Powershell spits out is:
"-Path
In Sound Mind [Generated].ps1 (example sound-track)
-Value
This is a text test."
I'm sure I'm doing something very wrong, sorry if its a really easy fix. Thanks! (Also I'm doing this on Linux)
Revised sample (thanks Theo):
# Initialize
$TRACK_COUNTER=0
$artist=Read-Host -Prompt "Artist/Band Name"
$language=Read-Host -Prompt "Language (use ISO 639-2 codes)"
$album_or_single=Read-Host -Prompt "Is this an Album/Sound-Track or a collection of Singles (0 for album/sound-track, 1 for single's)"
# Album
if ($album_or_single -eq 0) {
$album=Read-Host -Prompt "Name of the Album"
$date=Read-Host -Prompt "Enter the release date of the album (in YYYY-MM-DD format, or type nothing if unknown)"
$genre=Read-Host -Prompt "Genre of the Album"
$cover_art=Read-Host -Prompt "Select the audio/image file with the Cover Art (ex. MP3/M4A or JPG/PNG)"
Get-ChildItem "*.mp3"
$track_amount=Read-Host -Prompt "How many Tracks/Songs are in this Album"
New-Item "$album [Generated].ps1" -ItemType File
Add-Content -LiteralPath "$album [Generated].ps1" -Value 'This is a text test.'
}
# Singles
if ($album_or_single -eq 1) {
$track_amount=Read-Host -Prompt "How many seperate Single's are there"
}

Changing a variable based on output

Good Afternoon,
Just want to start by saying I am totally new with PS, and am really sorry if my approach to this is long winded or inaccurate.
I have a list of computernames that declared as individual Variables that my colleagues have provided me :
$Jon = "Abc1234"
$Mike = "Abc6789"
another 10 hostnames
I then ask the user if they want to send the files to another PC :
$Targetuser = Read-Host 'Who would you like to send it to?'
What I would like is the output from the above to change to the hostname, but I am unsure of how to do this.
Essentially, if the output was Mike, to change the $targetuser variable to $Abc6789
Thanks in advance
Dynamically named variables is almost always a bad idea. Instead, use a dictionary type, like a hashtable:
# Define user->host dictionary
$Hostnames = #{
Jon = "Abc1234"
Mike = "Abc6789"
}
# Ask user for target user
$TargetUser = Read-Host 'Who would you like to send it to?'
# Keep asking until the enter an actual user name
while(-not $Hostnames.ContainsKey($TargetUser)){
Write-Host "No hostname found for user '${targetUser}'!"
Write-Host "Choose one of:"
Write-Host ($Hostnames.Keys -join "`n")
$TargetUser = Read-Host 'Who would you like to send it to?'
}
# Get the associated hostname
$TargetHost = $Hostnames[$TargetUser]
# copy file to $TargetHost

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.

Is there a Module or Something Similar for Interactive Prompts in PowerShell?

Is there something we can use in PowerShell to ask users to select one item from an array of items? For example, I like how Inquirer.js can do it.
I have also seen PoshGui, but it seems too much work to create just a simple prompt.
The reason we want something similar is that we need to provide deployment scripts for our clients and make deployment guides as easy as possible. Asking users to select one item on a screen is much better than asking them to insert some guid to a config file.
Do you have any suggestions for user prompts for arrays?
You can also try ps-menu module:
https://www.powershellgallery.com/packages/ps-menu
Sample:
I've used the Out-GridView cmdlet for this in the past. When used with the -PassThru switch it allows the selected item to be passed to a variable. The example image you've shown, when written using Out-GridView (ogv if you want to use the alias) is:
$task = Read-Host -Prompt "What do you want to do?"
if ($task -eq "Order a pizza") {
$pizza_sizes = #('Jumbo','Large','Standard','Medium','Small','Micro')
$size = $pizza_sizes | Out-GridView -Title "What size do you need?" -PassThru
Write-Host "You have selected $size"
}
There are many considerations to take into account with this, the windows might not appear where you want them to and they may appear behind others. Also, this is a very simple example that obviously needs error handling and other aspects built in. I'd suggest some testing or to get a second opinion from others on SO.
You can be as creative as you like of course..
Here's a small function that builds a console menu:
function Simple-Menu {
Param(
[Parameter(Position=0, Mandatory=$True)]
[string[]]$MenuItems,
[string] $Title
)
$header = $null
if (![string]::IsNullOrWhiteSpace($Title)) {
$len = [math]::Max(($MenuItems | Measure-Object -Maximum -Property Length).Maximum, $Title.Length)
$header = '{0}{1}{2}' -f $Title, [Environment]::NewLine, ('-' * $len)
}
# possible choices: didits 1 to 9, characters A to Z
$choices = (49..57) + (65..90) | ForEach-Object { [char]$_ }
$i = 0
$items = ($MenuItems | ForEach-Object { '[{0}] {1}' -f $choices[$i++], $_ }) -join [Environment]::NewLine
# display the menu and return the chosen option
while ($true) {
cls
if ($header) { Write-Host $header -ForegroundColor Yellow }
Write-Host $items
Write-Host
$answer = (Read-Host -Prompt 'Please make your choice').ToUpper()
$index = $choices.IndexOf($answer[0])
if ($index -ge 0 -and $index -lt $MenuItems.Count) {
return $MenuItems[$index]
}
else {
Write-Warning "Invalid choice.. Please try again."
Start-Sleep -Seconds 2
}
}
}
You can use it like below:
$menu = 'Pizza', 'Steak', 'French Fries', 'Quit'
$eatThis = Simple-Menu -MenuItems $menu -Title "What would you like to eat?"
switch ($eatThis) {
'Pizza' {
$menu = 'Jumbo', 'Large', 'Standard', 'Medium', 'Small', 'Micro'
$eatThat = Simple-Menu -MenuItems $menu -Title "What size do you need?"
Write-Host "`r`nEnjoy your $eatThat $eatThis!`r`n" -ForegroundColor Green
}
'Steak' {
$menu = 'Well-done', 'Medium', 'Rare', 'Bloody', 'Raw'
$eatThat = Simple-Menu -MenuItems $menu -Title "How would you like it cooked?"
Write-Host "`r`nEnjoy your $eatThat $eatThis!`r`n" -ForegroundColor Green
}
'French fries' {
$menu = 'Mayonaise', 'Ketchup', 'Satay Sauce', 'Piccalilly'
$eatThat = Simple-Menu -MenuItems $menu -Title "What would you like on top?"
Write-Host "`r`nEnjoy your $eatThis with $eatThat!`r`n" -ForegroundColor Green
}
}
Result:
All of the answers are correct, but I also wrote a few reusable PowerShell helper functions. Readme. I auto-generate basic looking WinForms. Looks ugly, but works.
https://github.com/Zerg00s/powershell-forms
$selectedItem = Get-FormArrayItem (Get-ChildItem)
$Delete = Get-FormBinaryAnswer "Delete file?"
$newFileName = Get-FormStringInput "Enter new file name" -defaultValue "My new file"
# -------------------------------------------------------------------------------
# Prepare the list of inputs that user needs to populate using an interactive form
# -------------------------------------------------------------------------------
$preDeployInputs = #{
suffix = ""
SPSiteUrl = "https://ENTER_SHAREPOINT_SITE.sharepoint.com"
TimeZone = "Central Standard Time"
sendGridRegistrationEmail = "ENTER_VALID_EMAIL_ADDRESS"
sendGridRegistrationPassword = $sendGridPassword
sendGridRegistrationCompany = "Contoso & Tailspin"
sendGridRegistrationWebsite = "https://www.company.com"
fromEmail = "no-reply#DOMAIN.COM"
}
$preDeployInputs = Get-FormItemProperties -item $preDeployInputs -dialogTitle "Fill these required fields"
Unfortunately, there's little that is built in, and that little is hard to discover - see below.
Potentially providing a dedicated Read-Choice cmdlet or enhancing Read-Host is being discussed in GitHub issue #6571.
The $host.ui.PromptForChoice() method supports presenting a menu of choices, but it has limitations:
The choices are presented on a single line (which may wrap).
Only single-character selectors are supported.
The selector character must be a part of the menu-item text.
Submitting a selection always requires pressing Enter
A ? option is invariably offered, even if you don't want to / need to provide explanatory text for each menu item.
Here's an example:
# The list of choices to present.
# Specfiying a selector char. explicitly is mandatory; preceded it by '&'.
# Automating that process while avoiding duplicates requires significantly
# more effort.
# If you wanted to include an explanation for each item, selectable with "?",
# you'd have to create each choice with something like:
# [System.Management.Automation.Host.ChoiceDescription]::new("&Jumbo", "16`" pie")
$choices = '&Jumbo', '&Large', '&Standard', '&Medium', 'Sma&ll', 'M&icro'
# Prompt the user, who must type a selector character and press ENTER.
# * Each choice label is preceded by its selector enclosed in [...]; e.g.,
# '&Jumbo' -> '[J] Jumbo'
# * The last argument - 0 here - specifies the default index.
# * The default choice selector is printed in *yellow*.
# * Use -1 to indicate that no default should be provided
# (preventing empty/blank input).
# * An invalid choice typed by the user causes the prompt to be
# redisplayed (without a warning or error message).
$index = $host.ui.PromptForChoice("Choose a Size", "Type an index and press ENTER:", $choices, 0)
"You chose: $($choices[$index] -replace '&')"
This yields something like:

Printing ZPL (Zebra Programming language) file via Powershell

I wrote a Powershell script that asks questions, takes the user's input, plugs those variables in a label created in ZPL, then print it out via Out-Printer. Everything seems to work fine except the label that prints has incorrect spacing or missing data.
Originally I had the script create a .txt file that held the label in ZPL format. Checking the label all the data was correct. When I would print the .txt file from Notepad using the same printer (Driver was set to Generic/ Plain text). The label would be perfect.
I also tried switching the printing method to the WMI variant. It would do the same formatting issues and missing data, but now in different spots.
It almost seems Powershell is formatting the data before it prints.
Here is the current code I have with some data censored for privacy concerns.
$i = 0
$global:counter = $i + 1
$AllPrinters = gwmi win32_printer -computername $env:computername
$DefaultPrinterString = $AllPrinters | where {$_.Default -eq $true}
$DefaultPrinter = [regex]::match($DefaultPrinterString, '\"([^\"]+)\"').Groups[1].Value
Write-Host "========== Print Company Labels =========="
Write-Host "Question will be on the left, type in answer and hit ENTER"
$location = Read-Host "Which location? (LOC1 or LOC2)"
$pro = Read-Host "What is the Pro#?"
$quote = Read-Host "What is the quote number?"
$pallet = Read-Host "How many pallets are there?"
$printer = Read-Host "What printer do you want?"
While ($global:counter -le $pallet)
{
$printcode = "^XA^MCY^XZ^XA^SZ2^MMT^MTT~JSN^LT0^MD0^MNY^PR5^PON^PMN^CI0^LRN
^FT300,1300^A0B,350,160^FDPRO: $pro^FS
^FT480,1300^A0B,175,150^FD$location^FS
^FT480,1300^A0B,175,150^FD$location^FS
^FT480,900^A0B,175,100^FDQuote: $quote^FS
^FT800,1300^A0B,350,225^FDPallet $global:counter of $pallet^FS
^PQ01~*QUANT,04~,0,~*COPIES,04~,N^MCY^XZ"
(New-Object -ComObject WScript.Network).SetDefaultPrinter($printer)
$printcode | Out-Printer
Start-Sleep 1
(New-Object -ComObject WScript.Network).SetDefaultPrinter($DefaultPrinter)
$global:counter++
}
Any help would be greatly appreciated.
Thanks in advance