After writing a Powershell script to build a list of servers and then perform some maintenance activity on each, I decided to split it into two parts. The inner script, call it scriptB, does its thing on the server whose name is passed in; the outer script, scriptA, builds the list of servers and runs scriptB on each. The idea is that scriptA will someday be able to run a choice of scripts -- scriptB or scriptC, for instance -- against each server, depending on a control parm. And the maintenance script (B or C) can also be run by itself, i.e. by passing it the name of the target server.
I call scriptB from scriptA using invoke-command with the -filepath option, and this appears to work just fine. Except that, for each iteration, the content of scriptB appears in the output. If I call it three times then I have three copies of scriptB in the output. I already have write-output statements in scriptB that explain what's going on, but those messages are hard to spot amid all the noise.
I have tried assigning the output to a variable, for instance:
$eatthis = invoke-command -computername sqlbox -filepath c:\dba\scriptB.ps1
and then it was quiet, but the variable ate the good output along with the unwanted listings ... and it is large enough that I would prefer not to parse it. I tried reading up on streams, but that didn't look like a promising direction either. At this point I'm ready to convert scriptB to a function and give up the benefits of having it be a standalone script, but if anyone knows an easy way to suppress the listing of an invoke-command scriptblock specified via -filepath then that would be helpful.
Alternatively, a good way to phrase this for Google would be welcome. Search terms like "listing," "echo," and "suppress" aren't getting me what I need.
Convert your scripts into advanced functions. They can be stored in separate files and dotsourced in the master script. This loads the function and makes it available.
e.g.
c:\scripts\ComplicatedProcessfunctions.ps1
(which contains function Run-FunctionB {...} and later function RunFunctionC {...})
Then call the function
$dataResults = RunFunctionA
or even
$dataResults += RunFunctionA
if you're running within a loop and building a collection of results. Which sounds like you might be.
Make sure each function returns its data as an object or collection of objects. Probably a custom powershell object of your creation.
The master script then processes the results.
I would recommend Get-Help about_advanced_functions, the scripting guys blog, and the Powershell Scripting Games website for information on how to build advanced functions, and how to do it right.
Related
Imagine I have a program writen in whatever language and compiled to run interactivelly using just command line interface. Lets imagine this one just for the sake of simplify the question:
The program first asks the user its name.
Then based on some business logic, it may ask the user age OR the user email. Only one of those.
After that it finishes with success or error.
Now image that I want to write a script in powershell that fills all that data automatically.
How can I achieve this? How can I run this program, read its questions (outputs) and then provide the correct answer (input)?
If you don't know the questions it will ask ahead of time, this would be tough.
PowerShell scripts are normally linear. Once you start the program from in PowerShell, it would wait for the program to finish before continuing. There are ways to do things in parallel, but it doesn't interact like that.
Although if you're dealing with something like a website making the first call gives a response (completing the command). You could match the response to select the proper value.
Or if the program is local and allows command line parameters, you could do that.
This is a generic question, no code.
Not sure if I need to remove local variables as I thought it should be done by the Powershell Engine.
I had a script to gather info from WMI and used a lot of local variables. The output was messed up when running multiple times, but it got fixed after I clean up all local variables at the end of function/scriptblock.
Any thoughts/idea would be appreciated.
The trouble do not come from the fact that you do not remove your vars, but by at least from two beginers errors (or done by lazy developpers like me, supposing I'am a developper).
we forget to itialize our vars before using them.
we do not test every returns of our functions or CmdLets calls.
Once these two things done (code multipled by two at least) you can restart your script without cleaning anything except the processed datas.
For me scripting is most of the time done on a corner of a table even not push in a source repository.
So start scripting and ask yourself fewer questions.
9/10 times if you are trying to use the Invoke-Expression cmdlet, there is a better way. Building the arguments to a command dynamically? Use an array of arguments. Building the arguments to a cmdlet? Use splatting with an array or hashtable. Your command has a space in the path to it? Use the call operator (&).
This might seem open ended, but Invoke-Expression is an easily accessible cmdlet where the answer is almost always to never use it. But the cmdlet exists for a reason, is not deprecated, and most criticisms of its use state something similar to, "it's almost never the right answer", but never states when it is acceptable to use it. In what case is it acceptable to use Invoke-Expression? Or to word it a bit less openly, how was Invoke-Expression designed to be used?
To quote from a PowerShell team blog post titled Invoke-Expression considered harmful (emphasis added):
The bottom line: Invoke-Expression is a powerful and useful command for some scenarios such as creating new scripts at runtime, but in general, if you find yourself using Invoke-Expression, you should ask yourself, or maybe a respected colleague if there is a better way.
EBGreen notes:
Or to phrase it another way, It [Invoke-Expression] is ok to use as long as a user is never involved in any part of generating the string that will be invoked. But even then, not using it will enforce better habits than using it would.
In short:
As a matter of habit, always consider a different (usually more robust and secure) solution first.
If you do find that Invoke-Expression is your only choice, carefully consider the security implications: if a string from an (untrusted) outside source (e.g., user input) is passed directly to Invoke-Expression, arbitrary commands may be executed.
Therefore: Only use Invoke-Expression if you fully control or implicitly trust the input.
Note: As of Windows PowerShell v5.1 / PowerShell Core v6.1.0, the official Invoke-Expression help topic doesn't provide such guidance; this GitHub issue suggests rectifying that.
Rare examples of justified (safe) use of Invoke-Expression:
Creating PSv5+ custom classes dynamically:
so that the class can be used in a remote session.
so that the set of properties can be created based on conditions at runtime.
Using Invoke-Expression in combination with Write-Output:
to parse a string with embedded quoting, with extra precautions.
to parse command lines stored in a file, if trusted.
Using Invoke-Expression for nested property access:
via a property path stored in a string.
There is at least this question on how to initiate actions on shell closing. The problem is it doesn't catch closing powershell window with 'x' button. Apparently it doesn't generate said event. Is there any way to capture such close and force actions upon it?
What I want is to pipe some values upon closing from session variables (hashtable of System.Diagnostisc.Process) to system variables upon closing. So that new session can access them directly.
Perhaps the expression "Every problem can be solved with another level of indirection" (see also) applies here.
May really depend on your needs, and how your script(s) work, etc. Perhaps you could start each of your scripts that need this behavior by loading some common library, which exports a function that watches for the exit of a given PowerShell process, namely your current one.
You can get this info using the $pid variable and pass that to your watcher function which launches a process to wait for the exit of your current script. This could be another script, C#, etc. See also Get-Process -Id $pid.
Without knowing what you are trying to accomplish it is hard to recommend anything more specific that might meet your needs such as logging, transcripts, user education, detection of 'dirty shutdowns' of your code, etc.
I was told to write multiple actions using powershell script. Actions such as Apppool creation, SQL updation, File editing and etc.
I am going to write such a bulk thing in script first time.
So i would like to know the best practice before writting them.
Is it a good practice to write all the function in a single file?
I am thinking at least 10 functions i may need to write. Assuming each function may have 10 lines of code.
Consider modules: the simplest format is a manifest (.psd1) and a single script file (.psm1) containing all the functions, aliases, ... the module exports (plus any internal helpers).
In this case you are clearly putting multiple connected functions in one file. Even if much of the code is only dot-sourced into the script module they are still logically in one entity.
On the other hand using scripts in your path to execute without having to load before hand would tend (as per Adriano's comment to the question) to support one function (at script scope rather than a function statement) makes sense.
Therefore: there is no one "good practice": it all depends on the details of the circumstance.
Be pragmatic, truth come from action, no from words ;O)
So begin, by the beginining :
1) Does the thing you want to do exists somewhere on internet EX PoshCode (if so you can adapt it)
2) Think about your functions (not to much) object : reuse the code (write your algorith in pseudo code)
3) Use internet to look for the functions even existing
4) Wrote all functions in the same file as the main code to test them. During this phase you'll discover new functions and parameters to add or to remove from existing ones
5) Once you have tested your code, put the reusable functions (and the ones they depend on) into one or multiple module.
My solutions will be to create a custom Module where will be possible add function later.
You can save your single file with all functions as mymodule.psm1 in mymodule folder under this path $env:psmodulepath.
then add-module mymodule (or better call it in you $profile to have it ready when console is up)