need to learn how to get and use user input

by tommls at 2013-01-28 13:29:13

Can someone please direct me to what I should read to learn how to obtain user input and subsitute it into the below script to be run on four specific servers??

1) request user ID
2) for each of four named servers (x5,x6,x7,x8)
3) substitute in the entered user ID and run these three lines on each server

cd C:\Users\USERID\AppData\Local\Temp
md Low
ICACLS C:\Users\USERID\AppData\Local\Temp\Low /setintegritylevel (OI)(CI)low

With four servers it’s a pain to hit each server to run this…it needs to not matter which server the script is run from.

Thank you, Tom
by DonJ at 2013-01-28 13:34:35
You would normally create parameters.


[CmdletBinding()]
param(
[string]$UserID,
[ValidateCount(1,4)][string]$server
)
$server[0] # first server
$server[1] # second server
Set-Location "C:\Users$UserID\AppData\Local\Temp"
Mkdir "Low"
# etc


C:\MyScript.ps1 -UserID DonJ -Servers ONE,TWO,THREE,FOUR
by tommls at 2013-01-28 13:47:03
Thank you…where can I learn to make the script do a prompt for the user ID??

Prompt: Please enter user ID: ______________
I enter the ID, then it sets $UserID equal to what I entered at the prompt and runs the commands…

Thank you, tom
by DonJ at 2013-01-28 15:10:38
Add [parameter(mandatory=$true)] in front of [string].
by tommls at 2013-01-28 15:44:20
Thank you, this week I’ll work on figuring all this out…
I’ll try to remember to post finished script…
Thank you, Tom
by DonJ at 2013-01-28 16:28:02
Btw if you’re interested in really mastering this kind of thing look into my "Toolmaking" book. This is what it’s All about.
by tommls at 2013-01-29 11:59:23
Please make it a forum rule to always specify whether one is giving Powershell 2.0 or 3.0 code.
It’s not fair to give PS 3.0 code without saying it is PS 3.0 code to people unfamiliar with it and whose primary coding is PS 2.0.
Thank you, Tom
by DonJ at 2013-01-29 12:07:37
Folks don’t tend to read whatever rules we put up. I can’t even get folks to reliably use the CODE button to format their code.

If you’re asking about a specific version, I’d suggest you mention that when you post. If someone posts something that’s 3.0, they can generally provide a 2.0 equivalent if you ask. I don’t think anyone intends to be "unfair" to you. We wouldn’t know that you’re unfamiliar with 3.0 unless you said so; 3.0 is the latest shipping version, and we’d have no way of knowing what you’re coding in unless you offer that information.

I’m not sure if your comment was meant to be specific to this thread or not. The answer I gave you would work in either version.
by tommls at 2013-01-29 12:35:41
Good comments, thank you, I’ve been very frustrated with too many things today.
i’ve tried again, some code works, some not.
by DonJ at 2013-01-29 12:38:24
Well, if you can let me know what code doesn’t work for you, I’d be happy to try and help. As I said, what I’ve told you should work on either 2.0 or 3.0.
by tommls at 2013-01-29 12:43:45
cls
# $UserID = Read-Host "Please type the user ID:"
[CmdletBinding()]
param(
[parameter(mandatory=$true)][string]$UserID,
[ValidateCount(1,4)][string]$server
)
$server[0] = "x5" # first server
$server[1] = "x6" # second server
$server[2] = "x7" # third server
$server[3] = "x8" # fourth server

gives me these errors:

At D:\2013 PowerShell Scripts\IE_Printing.ps1:10 char:9
+ $server[ <<<< 2] = "x7" # third server
+ CategoryInfo : InvalidOperation: (2:Int32) , RuntimeException
+ FullyQualifiedErrorId : NullArray

Cannot index into a null array.
At D:\2013 PowerShell Scripts\IE_Printing.ps1:11 char:9
+ $server[ <<<< 3] = "x8" # fourth server
+ CategoryInfo : InvalidOperation: (3:Int32) , RuntimeException
+ FullyQualifiedErrorId : NullArray

Why would it error for the second two servers and not the first two??

I looked up validatecount and it’s correct == at least 1 server and up to 4 servers
Second two server lines are same as the first two except for name of server.

Thank you, Tom
by DonJ at 2013-01-29 12:54:34
Er… ok. So in your script, you should take out the "cls" command. [CmdletBinding()] really wants to come first.

And $server is set up as an input parameter - so you wouldn’t set that to something in the script. Let’s say I made a file named C:\Script.ps1, and it contained this:


[CmdletBinding()]
param(
[parameter(mandatory=$true)][string]$UserID,
[ValidateCount(1,4)][string]$server
)


I would execute that by running C:\Script.ps1 -Server x5,x6,x7,x8 -UserID DonJ. That passes those values into the parameters. Within the script, $server[0] would contain "x5" and so on. But because $server is defined as an input parameter, you’re not meant to change it within your script - you’re meant to just read whatever was passed in.

The errors are happening because $server was never dimensioned to be large enough. The ValidateCount() just validates what’s passed in via parameter when you run the script - it doesn’t define the variable as an array of a particular length. There’s a little but of under-the-hood stuff PowerShell does when binding parameters that’s making it a two-element array, which is why your first two commands worked.

If you wanted to provide x5, x6, x7, and x8 as default values for $server:


[ValidateCount(1,4)][string]$server = @(‘x5’,‘x6’,‘x7’,‘x8’)


Now, you can run C]. You’ll be prompted for -UserID, because it’s marked as mandatory. You won’t be prompted for -Server, because it isn’t mandatory. But $server will contain those four values because they were specified as a default. The @() construct denotes an array of values - technically, you could just do the comma-separated list without @(), but @() is considered better form.

Does that help? Keep in mind I don’t know what your background is, so if I’m over-explaining I apologize, and if I’m under-explaining, please ask questions.
by tommls at 2013-01-29 13:16:14
Thank you…problem now is that the ICACLS line does not process.

# how to run these on all four listed servers?? we only want to open script ones
Set-Location "C:\Users$UserID\AppData\Local\Temp"
Mkdir "Low"
ICACLS "C:\Users$UserID\AppData\Local\Temp\Low /setintegritylevel (OI)(CI)low"


It gives these errors (we need a check if the Low directory exists(

New-Item : Item with specified name C:\Users\tlyczko\AppData\Local\Temp\Low already exists.
At line:38 char:24
+ $scriptCmd = {& <<<< $wrappedCmd -Type Directory @PSBoundParameters }
+ CategoryInfo : ResourceExists: (C:\Users\tlyczko\AppData\Local\Temp\Low:String) [New-
Item], IOException
+ FullyQualifiedErrorId : DirectoryExist,Microsoft.PowerShell.Commands.NewItemCommand

icacls.exe : C:\Users\tlyczko\AppData\Local\Temp\Low /setintegritylevel (OI)(CI)low: The system cannot find the path specified.
At C:\IE_Printing.ps1:9 char:7
+ ICACLS <<<< "C:\Users$UserID\AppData\Local\Temp\Low /setintegritylevel (OI)(CI)low"
+ CategoryInfo : NotSpecified: (C:\Users\tlyczk…path specified.:String) , RemoteExc
eption
+ FullyQualifiedErrorId : NativeCommandError

Successfully processed 0 files; Failed processing 1 files

PURPOSE of this script is to enter UserID once and have it run the above three lines on all four servers.
It works properly if it’s run as a "DOS" batch file but doing so means RDP’ing into 4 servers and I’d like to only RDP in once and set UserID once even if it means running script multiple times.
It fixes the problem of Internet Explorer 9 printing blank pages that show only header and footer lines.
Therefore when it’s working properly it can be posted here for others to use.

Thank you, Tom
by DonJ at 2013-01-29 13:31:59
So, you can use the Test-Path command to see if a directory already exists.

If you have server names in $servers:

foreach ($server in $servers) {
# use $server - it contains only one server name
# you can still use $userid at this point also
}


As for the second error, I think you’re running into one of the difficulties in using external command-line tools inside PowerShell. It’s passing "C:\Users$UserID\AppData\Local\Temp\Low /setintegritylevel (OI)(CI)low" to Icacls.exe as a single parameter, because it’s all in quotation marks. Icacls.exe is complaining because "C:\Users$UserID\AppData\Local\Temp\Low /setintegritylevel (OI)(CI)low" isn’t a path. I don’t have the utility handy to test this, but you’re going to have to play with it. Try "C:\Users$UserID\AppData\Local\Temp\Low" /setintegritylevel (OI)(CI)low instead, and see if PowerShell figures out how to parse that.

In v3, there’s an easy trick to make that work, but it isn’t present in v2.
by tommls at 2013-01-29 14:14:23
I figured out ICACLS with

ICACLS "C:\Users$UserID\AppData\Local\Temp\Low" /setintegritylevel (OI)(CI)low
Now apparently I must learn more about remoting to get the script to run on the x5678 servers??? – presently it only runs locally. And it still throws an error about the directory but it processes anyway.

Thank you, Tom
by DonJ at 2013-01-29 14:18:10
Remoting does get pretty complex. But you don’t need it. Assuming your servers have the standard administrative shares (\x5\C$) you can just use that in your path instead of C:. UNCs work fine and Icacls should support them. Worst case, map a drive and run Icacls on that.

If you just want to suppress the error on New-Item, add -ErrorAction SilentlyContinue to the command.
by tommls at 2013-01-29 14:20:34
OIC – I will try all that. Thank you!! :slight_smile:
by sunnyc7 at 2013-01-29 15:32:50
You can always add this line in code while posting in forums.

Set-StrictMode -version latest
#Latest = 3.0 if it’s installed
Set-StrictMode -version 2.0
#Powershell version 2.0

But as Don says, I am not sure if people will follow it.
by tommls at 2013-01-29 15:45:13
I got the script mostly workng with the following, I could not figure out how to make the above-mentioned array suggestion actually touch all the four servers.
It still throws errors and the above suggestion about RunSilently did not work for me, I did not know where to put it.


[CmdletBinding()]
param(
[parameter(mandatory=$true)][string]$UserID
)
# run these on all four listed servers
Set-Location "\x5\C$\Users$UserID\AppData\Local\Temp"
Mkdir "Low"
ICACLS.EXE "\x5\C$\Users$UserID\AppData\Local\Temp\Low" /setintegritylevel (OI)(CI)low
Set-Location "\x6\C$\Users$UserID\AppData\Local\Temp"
Mkdir "Low"
ICACLS.EXE "\x6\C$\Users$UserID\AppData\Local\Temp\Low" /setintegritylevel (OI)(CI)low
Set-Location "\x7\C$\Users$UserID\AppData\Local\Temp"
Mkdir "Low"
ICACLS.EXE "\x7\C$\Users$UserID\AppData\Local\Temp\Low" /setintegritylevel (OI)(CI)low
Set-Location "\x8\C$\Users$UserID\AppData\Local\Temp"
Mkdir "Low"
ICACLS.EXE "\x8\C$\Users$UserID\AppData\Local\Temp\Low" /setintegritylevel (OI)(CI)low

Thank you, Tom
by DonJ at 2013-01-29 17:47:14
Ok, if you want to do it that way…

It isn’t "RunSilently." It’s -ErrorAction SilentlyContinue, and it’s available on all commands. It’s one of the common parameters, documented in about_common_parameters. You can add it to any command to suppress errors from that command.

In terms of the enumeration, it’s a ForEach loop. help about_foreach{/b] has the documentation. Since you’re moving the server list from being a parameter to being a hardcoded list…


[CmdletBinding()]
param(
[parameter(mandatory=$true)][string]$UserID
)
$servers = @(‘x5’,‘x6’,‘x7’,‘x8’)
foreach ($server in $servers) {
Set-Location "\$server\C$\Users\$UserID\AppData\Local\Temp&quot;<br> if (-not (Test-Path &quot;Low&quot;)) {<br> New-Item -Path &quot;Low&quot; -ItemType &quot;Directory&quot; -ErrorAction SilentlyContinue<br> }<br> icacls&#46;exe &quot;\\$server\C$\Users$UserID\AppData\Local\Temp\Low" /setintegritylevel (OI)(CI)low
}


I’m not certain if the backtick is needed in front of C$ but the $ has special meaning inside double quotes; using `$ forces it to be literal. I’m just in the habit of doing that so PowerShell doesn’t try and do something magical and mess me up.

As an aside… I think a lot of the trouble you may be having is just that you’re not familiar with scripting, or at least PowerShell’s variant of scripting. If you get some time to explore that when you’re not trying to solve a specific problem, I think it’ll ease up for you. Stick with it.

Good luck!
by tommls at 2013-01-30 05:53:24
I’ll try it as you suggested – it was just easier to copy/paste/edit etc. :slight_smile:
Thank you on the aside – you’re right, I am trying to do too much OJT.
Thank you for all your help etc.
Tom