Powershell answer file similar to Linux bash - powershell

What I want: Execute my powershell script (.ps1) and answer all the read-host questions using an answer file.
On Linux bash this is straightforward see:Answer file Linux bash

You can use a subexpression ($()) to call Read-Host in case a parameter argument is not present:
param(
$Name = $(Read-Host -Prompt "Input Name"),
$Company = $(Read-Host -Prompt "Input Company Name"),
$Country = $(Read-Host -Prompt "Country")
)
if(-not $Country){
$Country = "US"
}
Write-Host $(#'
Hello, {0} from {1}!
How is the weather in {2}?
'# -f $Name,$Company,$Country)
# do more stuff
Save as myscript.ps1.
Now, when you run it without any parameters, it prompts you for all the values:
PS C:\> .\prompt.ps1
Input Name: Arni
Input Company Name: MegaCorp Inc.
Country:
Hello, Arni from MegaCorp Inc.!
How is the weather in US?
PS C:\>
But if you want to run it without being prompted, just supply all required parameters:
PS C:\> .\prompt.ps1 -Name Xavier -Company "SuperCorp SA" -Country Brazil
Hello, Xavier from SuperCorp SA!
How is the weather in Brazil?
PS C:\>

What you are asking for does not exist in PowerShell, the methods of passing arguments to a function (e.g. splatting, the pipeline) cannot be used with Read-Host.
What is possible is creating your script as a function and making use of the aforementioned methods to pass the arguments when the function is to be run in an automated fashion.

Related

How to lowercase a variable expansion

I have the following code and variables declared. How do I make sure that $shared_mailbox_alias becomes lowercase when expanded to the Mailbox creation cmdlet below? Since it's based on $Project_code which is typed in capital letters:
$Project_number = Read-Host -Prompt 'What is the Project Number ie. 02XXX'
$Project_code = Read-Host -Prompt 'What is the Project Code (in capital letters) ie. CRE'
$Project_name = Read-Host -Prompt 'What is the Project extended Name ie. Victoria Station Platform'
$shared_mailbox_displayname = "File: $Project_name"
$shared_mailbox_name = "$Project_code"
$shared_mailbox_alias = "file.$Project_code"
New-Mailbox -Shared -DisplayName $shared_mailbox_displayname -Name $shared_mailbox_name -Alias $shared_mailbox_alias -PrimarySmtpAddress $shared_mailbox_primarysmtp
My goal is to have an alias of "file.abc" rather than "file.ABC", and I cannot change the way it is been given as input (ABC) since I have other cmdlets that create folders with that syntax.
Cheers
Solved, thanks for the input, this is the syntax I used:
$shared_mailbox_alias = "file.$($Project_code.ToLower())"

Power Shell Script for loop?

I am trying to create a script in PowerShell to run a utility with some default parameters. There will be multiple IP addresses into a CSV file that we will need to import. Then do a loop to run the utility for each IP address imported.
Set-Location 'C:\Program Files\Utility
$printerlist = Get-Content ".\Printer.csv"
$pass = Read-Host "Enter your Password"
.\Utility.exe -USERNAME="ADMIN" -PASSWORD="$pass"-ADDRESS="$printerlist" -PP-INSTALL="192.168.33.21" -JA-INSTALL="192.168.33.21" -IPA-INSTALL="192.168.33.21" -QUOTA-DELETE
Foreach($printer in $printerlist){
echo $line
}
As the comment points out, you need to loop over those IP addresses. You can use a feature called splatting to apply a set of default arguments:
Set-Location -Path $Env:ProgramFiles\Utility
$pass = Read-Host -Prompt 'Enter your Password'
$utilArgs = #(
'-USERNAME="ADMIN"'
"-PASSWORD=""$pass"""
'-PP-INSTALL="192.168.33.21"'
'-JA-INSTALL="192.168.33.21"'
'-IPA-INSTALL="192.168.33.21"'
'-QUOTA-DELETE'
)
foreach ($printer in Get-Content -Path Printer.csv) {
.\Utility.exe "-ADDRESS=""$printer""" #utilArgs
}
Docs
about_Splatting

Printing the argument in powershell

May I know, why the string argument is not printed in the following powershell script?
function Get-Name ( [string] $Username ) {
echo "user : $Username"
}
PS C:\> .\Get-Name.ps1 -username "test"
PS C:\>
The script file Get-Name.ps1 only defines the function Get-Name, it doesn't execute it.
Use the dot-source operator (.) to have it define the function in the calling scope, and then you can execute the function itself:
PS C:\> . .\Get-Name.ps1
PS C:\> Get-Name -Username test
user : test
Alternatively, remove the function Get-Name { and } part of the script as pointed out by Lee_Dailey, at which point the script file becomes a parameterized function in itself and you can then do:
PS C:\> .\Get-Name.ps1 -Username test
user : test
Please see the about_Scripts help file, especially the section about script scope and dot sourcing
the problem is that you define a function, not a callable script. [grin] this will work ...
Param ([string] $Username)
echo "user : $Username"
here's an example of calling the above ...
. .\Func_Get-Name.ps1 -username 'tutu'
output ...
user : tutu

Redirect standard input (read-host) to a Powershell script

Here's a sample powershell script:
$in = read-host -prompt "input"
write-host $in
Here's a sample 'test.txt' file:
hello
And we want to pass piped input to it from powershell. Here's some I have tried:
.\test.ps1 < test.txt
.\test.ps1 < .\test.txt
.\test.ps1 | test.txt
.\test.ps1 | .\test.txt
test.txt | .\test.ps1
.\test.txt | .\test.ps1
get-content .\test.txt | .\test.ps1
even just trying to echo input doesn't work either:
echo hi | \.test.ps1
Every example above that doesn't produce an error always prompts the user instead of accepting the piped input.
Note: My powershell version table says 4.0.-1.-1
Thanks
Edit/Result: To those looking for a solution, you cannot pipe input to a powershell script. You have to update your PS file. See the snippets below.
The issue is that your script \.test.ps1 is not expecting the value.
Try this:
param(
[parameter(ValueFromPipeline)]$string
)
# Edit: added if statement
if($string){
$in = "$string"
}else{
$in = read-host -prompt "input"
}
Write-Host $in
Alternatively, you can use the magic variable $input without a param part (I don't fully understand this so can't really recommend it):
Write-Host $input
You can't pipe input to Read-Host, but there should be no need to do so.
PowerShell doesn't support input redirection (<) yet. In practice this is not a (significant) limitation because a < b can be rewritten as b | a (i.e., send output of b as input to a).
PowerShell can prompt for input for a parameter if the parameter's value is missing and it is set as a mandatory attribute. For example:
function test {
param(
[parameter(Mandatory=$true)] [String] $TheValue
)
"You entered: $TheValue"
}
If you don't provide input for the $TheValue parameter, PowerShell will prompt for it.
In addition, you can specify that a parameter accepts pipeline input. Example:
function test {
param(
[parameter(ValueFromPipeline=$true)] [String] $TheValue
)
process {
foreach ( $item in $TheValue ) {
"Input: $item"
}
}
}
So if you write
"A","B","C" | test
The function will output the following:
Input: A
Input: B
Input: C
All of this is spelled out pretty concisely in the PowerShell documentation.
Yes; in Powershell 5.1 "<" is not implemented (which sucks)
so, this won't work: tenkeyf < c:\users\marcus\work\data.num
but,
this will: type c:\users\marcus\work\data.num | tenkeyf
...
PowerShell doesn’t have a redirection mechanism, but.NET have.
you can use [System.Diagnostics.Process] implements the purpose of redirecting input.
The relevant Microsoft documents are as follows.
Process Class
This is a sample program that works perfectly on my windows 10 computer
function RunAndInput{
$pi = [System.Diagnostics.ProcessStartInfo]::new()
$pi.FileName ="powershell"
$pi.Arguments = """$PSScriptRoot\receiver.ps1"""
$pi.UseShellExecute = $false
$pi.RedirectStandardInput = $true
$pi.RedirectStandardOutput = $true
$p = [System.Diagnostics.Process]::Start($pi)
$p.StandardInput.WriteLine("abc"+ "`r`n");
$p.StandardOutput.ReadToEnd()
$p.Kill()
}
RunAndInput
# OutPut
Please enter the information: abc
Received:abc
# receiver.ps1
('Received:' + (Read-Host -Prompt 'Please enter the information'))
Hope to help you!

Executing Powershell script from command line with quoted parameters

I am automating the build of a legacy MS Access application, and in one of the steps, I am trying to make an Access executable (.ADE). I have come up with the following code, which is stored in a file (PSLibrary.ps1):
Add-Type -AssemblyName Microsoft.Office.Interop.Access
function Access-Compile {
param (
[Parameter(Mandatory=$TRUE,Position=1)][string]$source,
[Parameter(Mandatory=$TRUE,Position=2)][string]$destination
)
Write-Output "Starting MS Access"
$access = New-Object -ComObject Access.Application
$access.Visible = $FALSE
$access.AutomationSecurity = 1
if (!(Test-Path $source)) { Throw "Source '$source' not found" }
if ((Test-Path $destination)) {
Write-Output "File '$destination' already exists - deleting..."
Remove-Item $destination
}
Write-Output "Compiling '$source' to '$destination'"
$result = $access.SysCmd(603, $source, $destination)
$result
Write-Output "Exiting MS Access"
$access.quit()
}
If I go into the PowerShell ISE and run the command below, then everything works fine, and the expected output is created:
PS C:>& "C:\Temp\PSLibrary.ps1"
PS C:>Access-Compile "C:\Working\Project.adp" "C:\Working\Project.ade"
However, I can't seem to generate the right hocus-pocus to get this running from the command line, as I would in an automated build. For instance,
powershell.exe -command "& \"C:\\Temp\\PSLibrary.ps1\" Access-Compile \"C:\\Temp\\Project.adp\" \"C:\\Temp\\Project.ade\""
What am I doing wrong?
For complex parameters, you can use Powershell's -EncodedCommand parameter. It will accept a Base64 encoded string. No escaping is needed for quotes, slashes and such.
Consider a test function that will print its parameters. Like so,
function Test-Function {
param (
[Parameter(Mandatory=$TRUE,Position=1)][string]$source,
[Parameter(Mandatory=$TRUE,Position=2)][string]$destination
)
write-host "src: $source"
write-host "dst: $destination"
}
Create command to load the script and some parameters. Like so,
# Load the script and call function with some parameters
. C:\Temp\Calling-Test.ps1; Test-Function "some\special:characters?" "`"c:\my path\with\spaces within.ext`""
After the command syntax is OK, encode it into Base64 form. Like so,
[System.Convert]::ToBase64String([System.Text.Encoding]::UNICODE.GetBytes('. C:\Temp\Calling-Test.ps1; Test-Function "some\special:characters?" "`"c:\my path\with\spaces within.ext`""'))
You'll get a Base64 string. Like so,
LgAgAEMAOgBcAFQAZQBtAHAAXABDAGEAbABsAGkAbgBnAC0AVABlAHMAdAAuAHAAcwAxADsAIAAgAFQAZQBzAHQALQBGAHUAbgBjAHQAaQBvAG4AIAAiAHMAbwBtAGUAXABzAHAAZQBjAGkAYQBsADoAYwBoAGEAcgBhAGMAdABlAHIAcwA/ACIAIAAiAGAAIgBjADoAXABtAHkAIABwAGEAdABoAFwAdwBpAHQAaABcAHMAcABhAGMAZQBzACAAdwBpAHQAaABpAG4ALgBlAHgAdABgACIAIgA=
Finally, start Powershell and pass the encoded string as a parameter. Like so,
# The parameter string here is abreviated for readability purposes.
# Don't do this in production
C:\>powershell -encodedcommand LgAgA...
Output
src: some\special:characters?
dst: "c:\my path\with\spaces within.ext"
Should you later want to reverse the Base64 encoding, pass it into decoding method. Like so,
$str = " LgAgA..."
[Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($str))
# Output
. C:\Temp\Calling-Test.ps1; Test-Function "some\special:characters?" "`"c:\my path\with\spaces within.ext`""
PowerShell like Bash can take single or double quotes
PS C:\Users\Steven> echo "hello"
hello
PS C:\Users\Steven> echo 'hello'
hello
this can alleviate some of the headache, also I think you can use the literal backslashes without escaping.
To run PowerShell, choose
Start Menu Programs Accessories
Windows Powershell Windows Powershell