Create a function with optional call variables - function

Is there a way to create a parameter in a PowerShell function where you have to call it in order to have it considered?
An example given by commandlet (the bold being what I want to do):
Invoke-Command -computername Server01 -Scriptblock {...}
Here is an example of what I want to do with the function
Function DoStuff($computername, -arg2, -domain $domain)
Test-parameter(-domain) if (-domain -eq $true) {
use $domain
}
Else {
$domain = "Domain1"
}
test-parameter($arg2) {
if ($arg2 -eq $true) {
Do something
}
else {
Do the opposite
}
}
So in summary:
If "-arg2" is present, I want something to happen in the script. If "-Domain" is present and has an argument with it, I want that to be used rather then the set argument.

Powershell provides a lot of built-in support for common parameter scenarios, including mandatory parameters, optional parameters, "switch" (aka flag) parameters, and "parameter sets."
By default, all parameters are optional. The most basic approach is to simply check each one for $null, then implement whatever logic you want from there. This is basically what you have already shown in your sample code.
If you want to learn about all of the special support that Powershell can give you, check out these links:
about_Functions
about_Functions_Advanced
about_Functions_Advanced_Parameters

I don't think your question is very clear, this code assumes that if you're going to include the -domain parameter, it's always 'named' (i.e. dostuff computername arg2 -domain domain); this also makes the computername parameter mandatory.
Function DoStuff(){
param(
[Parameter(Mandatory=$true)][string]$computername,
[Parameter(Mandatory=$false)][string]$arg2,
[Parameter(Mandatory=$false)][string]$domain
)
if(!($domain)){
$domain = 'domain1'
}
write-host $domain
if($arg2){
write-host "arg2 present... executing script block"
}
else{
write-host "arg2 missing... exiting or whatever"
}
}

Not sure I understand the question correctly.
From what I gather, you want to be able to assign a value to Domain if it is null and also what to check if $args2 is supplied and according to the value, execute a certain code?
I changed the code to reassemble the assumptions made above.
Function DoStuff($computername, $arg2, $domain)
{
if($domain -ne $null)
{
$domain = "Domain1"
}
if($arg2 -eq $null)
{
}
else
{
}
}
DoStuff -computername "Test" -arg2 "" -domain "Domain2"
DoStuff -computername "Test" -arg2 "Test" -domain ""
DoStuff -computername "Test" -domain "Domain2"
DoStuff -computername "Test" -arg2 "Domain2"
Did that help?

Related

Powershell startjob import-module call function with arguments

So I am trying to start-job from a module I wrote.
Copy-Modules.psm1
function startcopy([string] $ShowToCopy) {
if (-not($ShowToCopy)) { return "No name provided. Doing nothing." }
} else { return "Name Provided $ShowToCopy" }
}
in the main script I am calling it as follows:
$Copyname = "test"
Start-Job -Name "copy1" -InitializationScript { Import-Module -Name .\Copy-Modules.psm1 } -ScriptBlock {startcopy} -ArgumentList $Copyname
However the arguments never seems to go through. No matter how I format or pass the argument with switch or without I always get the result No name provided. Doing nothing.
The simplest solution - assuming you need no other functions from your Copy-Modules.psm1 module - is to pass your function's body as Start-Job's -ScriptBlock argument:
Start-Job -Name "copy1" -ScriptBlock $function:startcopy -ArgumentList $Copyname
$function:startcopy uses namespace variable notation to get the startcopy's body as a script block.
Note:
This obviates the need to define your startcopy function in the scope of the background job (which is an independent session in a child process that knows nothing about the caller's state), which is what your -InitializationScript script block does.
The only limitation of this approach is that the script block won't be named, i.e. the original function name is lost, and $MyInvocation.MyCommand.Name inside the function returns the empty string.
As for what you tried:
It is the script block as a whole that receives the (invariably positional arguments passed to -ArgumentList, which you'll have to pass on explicitly to any commands called inside the script block, using the automatic $args variable:
$Copyname = "test"
# Note the use of $args[0]
Start-Job -Name "copy1" `
-InitializationScript { Import-Module -Name .\Copy-Modules.psm1 } `
-ScriptBlock { startcopy $args[0] } -ArgumentList $Copyname

how to call a variable from a function

function test{
for($a=0;$a -le 2;$a++){
if($a -eq 1){break}
}
}
#----outside----
write-output $a
How to call a variable from a function and not to use return to get $a ?
Aside from using scopes as described in Rob's answer, there is also the possibility to send a parameter by reference.
This means that inside the function, a reference to the original variable is used, so whatever the function does with it, the original value is affected.
The downside is that when using references, you must use the Value property of the System.Management.Automation.PSReference type to access/alter your data as in the example below:
function test ([ref]$a) {
for ($a.Value = 0; $a.Value -le 2; $a.Value++){
if ($a.Value -eq 1) {break}
}
}
#----outside----
$a = 0
test ([ref]$a) # the parameter needs to be wrapped inside brackets and prefixed by [ref]
Write-Output $a
When sending a parameter that is an object type, like for instance a Hashtable, then by default it is always passed to the function by reference and for those you don't use the [ref] accellerator.
Have you tried to set the variable global?
$var="Test"
function test()
{
$global:var="blub"
}
test
$var
or
$a = 0
function test{
for($global:a=0;$global:a -le 2;$global:a++){
if($global:a -eq 1){break}
}
}
test
write-output $a

Powershell parameter passing to function

I'm facing with an annoying problem: my script seemingly doesn't pass any argument to a function I've defined.
$server = 'http://127.0.0.1:8080'
Function Get-WorkingDirectory([string]$address)
{
#echo $address
$content = Get-Content -path C:\....\file.txt
$content -contains $address
} #end Get-WorkingDirectory function
if(Get-WorkingDirectory $server)
{
echo "works"
}
else
{
echo "error"
}
It is stuck on "works". If I try to echo address in the function, it is empty.
What the heck I'm doing wrong?! I know this is a pretty noobish question, but I tried everything I found on the net.
Thanks in advance for help!
echo is an alias for Write-Output but as you are using the output of the function in the if statement, nothing gets shown.
For testing purposes, use Write-Host in this instance to show the variable being passed correctly.
$server = 'http://127.0.0.1:8080'
Function Get-WorkingDirectory([string]$address)
{
write-host "$address using write host"
} #end Get-WorkingDirectory function
if (Get-WorkingDirectory $server) {
}
Output of Get-WorkingDirectory is shadowed by if statement.
Try to use it without if and you'll see that argument is passed correctly. For example,
$server = 'http://127.0.0.1:8080'
Function Get-WorkingDirectory([string]$address)
{
Write-Host $address
}
Get-WorkingDirectory $server
Address is printed well

PowerShell Functions and varying parameters

Early on in my script I have check to define whether a parameter "-Silent" was used when running the script. The idea is to have zero output from the script if it was and it will be checked on every Write-Host entry I have later. It seems a bit heavy to make if-else statements on every single Write-Host I have, so I decided to go with a function - something like this:
Function Silent-Write ([string]$arg1)
{
if ($silent -eq $false) {
if ($args -ieq "-nonewline") {
Write-Host "$arg1" -NoNewLine
}
elseif ($args -ieq "-foregroundcolor") {
Write-Host "$arg1" -ForegroundColor $args
}
else {
Write-Host "$arg1"
}
}
}
Silent-Write -ForegroundColor red "hello"
This is not working, but you get the idea; besides passing the text I want to output, Silent-Write function should also take other Write-Host arguments into consideration. Quite simple issue I believe, but something I cannot figure out with the knowledge of functions I have.
In PowerShell V3 you can use splatting:
Function Silent-Write
{
if (!$silent) {
Write-Host #args
}
}
Silent-Write -ForegroundColor red "hello"

How do I dynamically create functions that are accessible in a parent scope?

Here is an example:
function ChildF()
{
#Creating new function dynamically
$DynFEx =
#"
function DynF()
{
"Hello DynF"
}
"#
Invoke-Expression $DynFEx
#Calling in ChildF scope Works
DynF
}
ChildF
#Calling in parent scope doesn't. It doesn't exist here
DynF
I was wondering whether you could define DynF in such a way that it is "visible" outside of ChildF.
Another option would be to use the Set-Item -Path function:global:ChildFunction -Value {...}
Using Set-Item, you can pass either a string or a script block to value for the function's definition.
The other solutions are better answers to the specific question. That said, it's good to learn the most general way to create global variables:
# inner scope
Set-Variable -name DynFEx -value 'function DynF() {"Hello DynF"}' -scope global
# somewhere other scope
Invoke-Expression $dynfex
DynF
Read 'help about_Scopes' for tons more info.
You can scope the function with the global keyword:
function global:DynF {...}
A more correct and functional way to do this would be to return the function body as a script block and then recompose it.
function ChildF() {
function DynF() {
"Hello DynF"
}
return ${function:DynF}
}
$DynFEx = ChildF
Invoke-Expression -Command "function DynF { $DynFEx }"
DynF
Thanks to Richard's post. Kept having issues doing this simple thing. I revised for passing a function from local to remote.
#Method 1 Load the function from disk
$getCert = gc 'C:\MyScripts\getCert.ps1'
Invoke-Command $RemoteSrv -ScriptBlock {Set-Variable -name DefFN -value ($Args -join "`n") -scope global ; Invoke-Expression $DefFn } -ArgumentList $getCert
#Method 2 Load the function from local definition of function
Invoke-Command $RemoteSrv -ScriptBlock {Set-Variable -name DefFN -value ($Args -join "`n") -scope global ; Invoke-Expression $DefFn } -ArgumentList ('Function GetCert {'+(Get-Command GetCert).Definition+'}')
#Remote server now has function
Invoke-Command $RemoteSrv -ScriptBlock {getcert stackoverflow.com}
URL : stackoverflow.com
Expires : 12/14/2021 8:07:08 AM
SAN : DNS Name=*.askubuntu.com, DNS Name=.....
Thumbprint : ec0055be478411bafe98d11d63a5c9279ff0e173
IP : 151.101.193.69
Handle : 2866249748176
Issuer : CN=R3, O=Let's Encrypt, C=US
Subject : CN=*.stackexchange.com