(PowerShell) Plugging user input into cmd argument - powershell

So I am fairly new at PowerShell and I am trying to do something that seems like it should be very simple but I have not been able to find any way to do it or anything on the internet. Honestly I think that might be more because I am unfamiliar with the terminology. Anyways here goes:
All I am trying to do is get Set-Service to use user input to target which service it would like to use. I thought this would be as simple as
Start-Service -Name $Services
But it always comes back as null.
and I am using
$Services = Read-Host "Service Name"
to define $Service, also I have attempted to use $Service as a string which seemed like it was causing the issue but it always came back as string empty.
I believe this is because I am not using the correct data type but I am unsure. If someone could help me with this simple problem I would greatly applicate it. It really annoying that something that seems like it should be very simple is causing me so much trouble.
Thanks,
Edit: Also I should add the full line I am attempting is
Invoke-Command -ComputerName $Server -Credential $UserCreds
-ScriptBlock {Start-Service -Name $Services}

The problem is that your remote script can't access the local variable. That's why you should always test something in the most simple scenario possible to see if it works. Here the problem lies not in the Read-Host or the variable, like you are assuming.
To make local variables available to the remotely running script, you need to to this:
Invoke-Command -ComputerName $Server -Credential $UserCreds -ScriptBlock { param($Services) Start-Service -Name $Services } -ArgumentList $Services
Have a look here for more details:
https://technet.microsoft.com/en-us/library/hh849719.aspx

Related

Get version of Installed Software on remote machine

I am trying a write an application to fetch the version of the application installed on remote machines. There is a need to query many remote servers and get the version of the application and show it on the dashboard.
Powershell WMI takes too long to get the information. I am looking for something lot faster.
The app will read remote server information like IP, Username, and password from a config file and fetch the data.
Any help is really appreciated.
It sounds like you want to take a closer look at Powershell Sessions.
There are at least two ways to approach in from there, one is using Invoke-Command in combination with the -ComputerName attribute, possibly along with -Authentication or -Credential. -ScriptBlock contains the code you want to run.
Invoke-Command -ComputerName "computername.domain.local" -ScriptBlock { ... }
I assume from "the application" that your concern is one application, and not every application. Then you should be able to tell the version by running Get-Item on the executable, then look at either VersionInfo.ProductVersion or VersionInfo.FileVersion, whichever is more relevant to your case.
To access one of them, you could use something like:
$version = (Get-Item "path-to-executable\executable.exe').VersionInfo.ProductVersion
To find out which attributes are relevant to your executable, you can run
Get-Item "executable.exe" | Select -ExpandProperty VersionInfo | Format-List *
Combining these techniques, you could try something like this.
# this is a dummy array for example purposes
$computers = #(#{'ip' = '127.0.0.1'; 'username' = 'admin'; 'password' = 'password'})
foreach($computer in $computers)
{
# creating a PSCredential object from plain text passwords is not a good practice, but I'm assuming here that's what you've got to work with
$credentials = [System.Management.Automation.PSCredential]::new($computer.username, (ConvertTo-SecureString -String $computer.password -AsPlainText -Force))
# fetch versioninfo info from remote computer
$versioninfo = Invoke-Command -ComputerName $computer.ip -Credential $credentials -ScriptBlock { Get-Item "executable.exe" | Select -ExpandProperty VersionInfo
if ($versioninfo.ProductVersion -ne '3.1.2414.0')
{
# do something if product version isn't 3.1.2414.0
}
if ($versioninfo.ProductVersionRaw.Major -lt 5)
{
# do something if product version major part is less than 5 (true for 1.5.5.5 but false for 5.1.1.1)
}
}
If you want to run several commands on the client computers, use New-PSSession and pass the session along to every call to Invoke-Command, otherwise you'd lose time and resources opening a new session every time.
Here's an example on how that could be achieved:
$session = New-PSSession -ComputerName $computer.ip -Credential $credentials
$versioninfo = Invoke-Command -Session $session -ScriptBlock { # do something }
if ($versioninfo.ProductVersion -lt 1)
{
Invoke-Command -Session $session -ScriptBlock { # do something else }
}
Remove-PSSession -Session $session
You might also want to check out the using: scope modifier if you find a need to pass variables along to the remote computer, which would make $localvariable visible at the remote computer with $using:localvariable (readonly)
If time is still a concern after this (especially with tcp timeouts on offline hosts), then threading is the next topic you'd want to look into.
As far as I know, my code is compatible with Powershell v3.1, but I recommend using no less than v5, especially on the machine running the script.
This should be enough information to send you on your way. Good luck. :)

Using Parameters when Calling Script with Start-Job

I am working on a script that must change users in the middle of running in order to be able to access a network folder. I have figured out how to get the credentials working, but now cannot understand how to pass parameters to the second script that is being called. The code that I currently have:
$myJob = Start-Job -ScriptBlock {& "\\my\folder\path\script.ps1" -serverName $serverName -serverInstance $serverInstance} -Credential $cred
$myJob | Wait-Job
$myJob | Receive-Job -Keep
I need to pass the serverName and serverInstance variables to the script that Start-Job is running, while also still being able to use credential. Is there a way to do this?
I have investigated Invoke-Command and Invoke-Expression, but neither of those fit this situation. Invoke-Command doesn't work with remote computers/drives and Invoke-Expression doesn't work with credentials. I tried the answer that was provided here, but that would not correctly pass in the parameters either.
Any help is much appreciated as I have been working on this problem for a few hours now. I am sure I am missing something obvious.
You can use the using scope modifier provided you are on PowerShell version 3 or higher:
$myJob = Start-Job -ScriptBlock {& "\\my\folder\path\script.ps1" -serverName $using:serverName -serverInstance $using:serverInstance}
You can also use local variables in remote commands, but you must indicate that the variable is defined in the local session. Beginning in Windows PowerShell 3.0, you can use the Using scope modifier to identify a local variable in a remote command. The syntax of Using is as follows:
$Using:<VariableName>
If you are on PowerShell version 2, you will need to utilize the -ArgumentList parameter and then modify your scriptblock to accept the arguments that are passed. Avshalom comments on one way to do this.
See About_Remote_Variables for more information.

How to Properly Put together a Function in Powershell

I have done a ton of research on how to push this EXE to a remote PC using PSSession and all lines of code work when executed line by line. But i have had a hard time putting this into a function that makes sense and will execute all lines of code and successfully install the software with one push of a button. Not sure whats happening. It will install the exe locally when i tried to do put all lines of code in a function and run it. Can you please help instruct what i am doing wrong? Sorry i am a newbie at Powershell.
$dc1 = New-PSSession -ComputerName DC1
Copy-Item C:\TPAdmin\Greenshot-INSTALLER-1.2.10.6-RELEASE.exe -Destination C:\TPAdmin -ToSession $dc1
Enter-PSSession -Session $dc1
Invoke-Command -ScriptBlock {C:\TPAdmin\Greenshot-INSTALLER-1.2.10.6-RELEASE.exe /VERYSILENT /LOG="C:\SOFTWAREINSTALL.LOG"
Remove-Pssession $dc1
Enter-PSSession is for interactive use only, so not suitable for use in a function.[1]
Instead of using Enter-PSSession, pass the session you've created with New-Session to the Invoke-Command command's -Session parameter, which will run the command in the context of that (remote) session.
# Define your function...
function Invoke-InstallerRemotely {
param([string] $ComputerName)
$session = New-PSSession -ComputerName $ComputerName
Copy-Item C:\TPAdmin\Greenshot-INSTALLER-1.2.10.6-RELEASE.exe -Destination C:\TPAdmin -ToSession $session
# Do NOT use Enter-PSSession.
# Pass your session to Invoke-Command with -Session
Invoke-Command -Session $session -ScriptBlock {C:\TPAdmin\Greenshot-INSTALLER-1.2.10.6-RELEASE.exe /VERYSILENT /LOG="C:\SOFTWAREINSTALL.LOG"
Remove-PSSession $session
}
# ... then invoke it.
# Note that functions must be defined *before* they're called.
Invoke-InstallerRemotely -ComputerName DC1
[1] Using it in a function means that an interactive session on the target computer is entered, which you must exit interactively (by typing and submitting exit or Exit-PSSession) before the remaining statements in the function are executed, again locally.
As for …
Sorry i am a newbie at Powershell.
… that's all fine, as we all had to start from somewhere. However... a couple of things here:
Please be sure to format your posts, to encourage folks to want to
help. People frown on no doing that. Having to copy, paste and
reformat your post is well, extra unnecessary work. ;-}. We've al been there.
We have no idea how you are getting up to speed on PowerShell, but
use the freely available resources to limit/avoid all the
misconceptions, frustrations, errors, potential bad habits, etc.,
that you are going to encounter. Live on watching the videos on:
YouTube
Microsoft Virtual Academy
MSDN Channel9
Microsoft Learn
as well as the reference
and eBook resources.
Back to your use case. You do not say what is happening. So, you leave us to guess. Which is not really potentially helpful to you.
Nonetheless, you should just need to do this... in PowerShell v5x as it's require to use the -ToSession argument.
$DC1 = New-PSSession -ComputerName 'DC1'
Copy-Item -ToSession $DC1 -Path 'C:\TPAdmin\Greenshot-INSTALLER-1.2.10.6-RELEASE.exe' -Destination 'C:\TPAdmin'
Invoke-Command -Session $DC1 -ScriptBlock {C:\TPAdmin\Greenshot-INSTALLER-1.2.10.6-RELEASE.exe /VERYSILENT /LOG="C:\SOFTWAREINSTALL.LOG"}
Remove-PSSession -Session $DC1
I am not sure why you are doing that Enter-PSSsssion in the New-PSSession command as it is not needed. It's for standalone interactive sessions.
Explicit PSRemoting = Enter=PSSEssion
Implicit PSREmoting = New-PSSEssion
If all else fails for you on the copy via the session, then just use the normal UNC way to copy from source to destination.
Copy-Item -Path 'C:\temp\Results.csv' -Destination "\\$($DC1.Computername)\c$\temp"
See also:
Copy To or From a PowerShell Session

Trouble passing parameters to a scriptblock

I am having alot of difficulty passing parameters to a script block in powershell.
$delScript={del C:\DateResults\* $args[0] $args[1] }
$result0 = Invoke-Command -ComputerName $targetServer.TrimStart("\\") -Credential $credentials -ScriptBlock $delScript -ArgumentList #("/q" , "/s")
I am starting to lose the plot trying to please the syntax hell of powershell and it's script block. I have researched this problem to death and I cannot even seem to get this basic problem working. I was hoping after spending the better part of 4hrs on this problem someone on SO could help me.
Thanks in advance!
You are having an issue since you are trying to run a old dos command that PowerShell created an alias for to help ease you into powershell. As mentioned in the comment del is an alias for Remove-Item. Remove-Item only accepts one positional argument which is -Path. To allow your script to work as is you should just be able change your $delScript to this
$delScript={cmd.exe /C del C:\DateResults\* $args[0] $args[1] }
Which would run your code the way you would expect. While I was typing this you already figured out the better approach which is to use the native Remove-Item and remove your -ArgumentList from Invoke-Command
$delScript={Remove-Item C:\DateResults\* -Recurse -Force}
I would also recommend you check Get-Alias to see other so you don't get yourself caught again.
Sorry, it was down to my own ignorance and stupidity. Hovering over 'del' in PowerGUI should have told me everything I needed to know.
Below is all I needed in the end.
$delScript={del C:\DateResults\* -Recurse -Force }
$result0 = Invoke-Command -ComputerName $targetServer.TrimStart("\\") -Credential $credentials -ScriptBlock $delScript

Installing Windows Features on remote server 2012 using powershell 3.0

I am wondering which is best practice considering both examples will probably work. Using the built in help examples I have written a script to install windows features on remote servers. Here is my code:
$servers = ('server1', 'server2', 'server3', 'server4')
ForEach ($server in $servers) {
Install-WindowsFeature -Name Desktop-Experience -ComputerName $server -IncludeAllSubFeature -IncludeManagementTools -Restart
}
Would the above be preferred OR should I wrap the "Install-WindowsFeature ..." in an "Invoke-Command" block like the following?
Invoke-Command -ComputerName server1, server2, server3, server4 -command {
Install-WindowsFeature -Name Desktop-Experience -ComputerName $server -IncludeAllSubFeature -IncludeManagementTools -Restart
}
Thanks for your insight!
Personally I would use the latter (directly call Install-WindowsFeature -ComputerName $server rather than do a separate Invoke-Command) in this case for the following reasons:
You may be hard-coding the feature names now, but in the future you may want to put those in a variable. If you put them in a variable, you'll have to pass it as a parameter into the Invoke-Command's script block. This is entirely possible, but more work.
By using your own loop, you can write progress messages, logging, etc.
You gain nothing by using Invoke-Command in this case because you're running a single command on the remote computer (as opposed to running multiple commands with -ComputerName parameters vs. running multiple commands inside the script block).