I am having some issue in calling a function using named parameters.
This is the declaration of the function in a separate file (Security.ps1):
function Add-SSRSItemSecurity
(
[Parameter(Position=0,Mandatory=$false)]
[Alias("SSRSrange")]
[string]$range,[Parameter(Position=1,Mandatory=$false)]
[Alias("path")]
[string]$itemPath,
[Parameter(Position=2,Mandatory=$false)]
[Alias("grp")]
[string]$groupUserName,
[Parameter(Position=3,Mandatory=$false)]
[Alias("SSRSrole")]
[string]$role,
[Parameter(Position=2)]
[bool]$inherit=$true
)
I then call this function in another Host.ps1 script as:
Set-Location 'C:\SSRSJobs'
. .\SSRSsecurity.ps1
This call works in the Host file:
Add-SSRSItemSecurity -range "server1" -itemPath "/Test" -groupUserName "CN\Group" -role "Browser"
I tried to pass in multiple parameters to the function as a loop, but calling new variables each time:
$securityArray = #()
$securityArray = Get-Content -Path "C\ReleaseSecurity.txt"
foreach($line in $securityArray)
{
Add-SSRSItemSecurity $line;
}
The file having:
-range "server1" -itemPath "/Test" -groupUserName "CN\Group" -role "Browser"
-range "server2" -itemPath "/Test" -groupUserName "CN\Group" -role "Browser"
-range "server3" -itemPath "/Test" -groupUserName "CN\Group" -role "Browser"
The error I get is:
Add-SSRSItemSecurity : Cannot bind positional parameters because no names were given.
At line:229 char:27
+ Add-SSRSItemSecurity <<<< $line;
+ CategoryInfo : InvalidArgument: (:) [Add-SSRSItemSecurity], ParameterBindingExcepti
on
+ FullyQualifiedErrorId : AmbiguousPositionalParameterNoName,Add-SSRSItemSecurity
Inspecting the string, the $line variable does hold correct naming for parameters. I've tried all sorts of error trapping, but I'm unable to get a decent error message other than the above. I've also tried forms of quoting, but I cannot get the function to see the name binding.
Can multiple variables be called in a function that are bound to just a PowerShell variable name?
You can use splatting for that. Save the parameters as a CSV like this:
"range","itemPath","groupUserName","role"
"server1","/Test","CN\Group","Browser"
"server2","/Test","CN\Group","Browser"
"server3","/Test","CN\Group","Browser"
and load it like this:
Import-Csv 'C:\ReleaseSecurity.txt' | % {
Add-SSRSItemSecurity #_
}
Related
I am trying to write a function that echo's the input if my script is running in debug mode.
[bool]$debugmode = $true
#one liner version for manually pasting into the powershell console
#Function DebugWrite-Output([bool]$isDebug,$inputObject){if ($isDebug -eq $true){Write-Output $inputObject}}
Function DebugWrite-Output([bool]$isDebug,$inputObject)
{if ($isDebug -eq $true){
Write-Output $inputObject
}
}
DebugWrite-Output -isDebug = $debugmode -inputObject "Loading create access file function"
the error i get is
DebugWrite-Output : Cannot process argument transformation on parameter 'isDebug'. Cannot convert value "System.String" to type "System.Boolean". Boolean parameters accept only Boolean values and numbers, such as
$True, $False, 1 or 0.
At C:\Users\*****\source\repos\Powershell Scripts\Modular-Export.ps1:9 char:28
+ DebugWrite-Output -isDebug = $debugmode -inputObject "Loading create ...
+ ~
+ CategoryInfo : InvalidData: (:) [DebugWrite-Output], ParameterBindingArgumentTransformationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,DebugWrite-Output
This error doesnt make sense to me since i am passing a boolean into the boolean and a string into the psobject.
You have two options
Correct your syntax error: You must pass parameters in the form of -param [value]:
-isDebug $debugmode
Use the right tool for the job, a [switch] parameter:
function DebugWrite-Output([switch] $isDebug, $inputObject) {
if ($isDebug.IsPresent) { ...
Then you call it by just including the switch:
-isDebug
The PowerShell version I am using is 5.0. I have a situation where I need to use a function and call it within a switch statement. My work involves to do couple of operations.
In brief, my scenario is the inputs for the switch statement are two A and B. I have to call a function declared in switch "condition A" in switch "conditionB", I mean execute the same set of operation.
Exception:
installsw is not recognized as the name of a cmdlet, function, script file,
or operable program. Check the spelling of the name,
CategoryInfo : ObjectNotFound: (regupgrade:String) [], CommandNotFoundException
FullyQualifiedErrorId : CommandNotFoundException
$condi = Read-Host -Prompt "Enter the input"
switch ($condi) {
A {
function installsw() {
Write-Host "install necessary sw"
install some s/w using "Start-Process" Command
}
installsw
}
B {
Write-Host "s/w upgrade"
installsw
$logs = Copy-Item -Path"D:/var" -Destination "D:/Temp"
}
}
Define installsw before the switch. Unless $condi is 'A', the installsw function won't be defined.
Btw, you can run any pipeline inside the first switch line:
switch (Read-Host -Prompt "Enter the input") {
This is driving me crazy I have spent the last few days trying to find some answers in the forums but not seen anything which can help me here. Maybe I am just blind.
Here is my problem. I am trying to update one of our Confluence Wiki pages using the API provided to update a page.
I have three scripts or functions:
Deploy script or controller script which calls the Create and with the response (Json) calls Update
Create Confluence Json
Update page
This is Create function
function Create-WikiPage
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[int]$CurrentPageRevisionNumber
)
# Creates the ID for new page
$NextPageID=$CurrentPageRevisionNumber + 1
# Creates the json body of the Wiki page
$ToolsPage= #{
"version"= #{
"number"= $NextPageID
};
"title"= "Windows Build Agent Tool Set";
"type"= "page";
"body"= #{
"storage"= #{
"value"= "<p><table><tr><th>Vendor-Application</th><th>Version</th></tr></table></p>";
"representation"= "storage"
}
}
} | ConvertTo-Json
$ToolsPage
}
The update function looks like this:
Update-WikiPage {
[CmdletBinding()]
param(
[ValidateNotNullOrEmpty()]
[string]$Server,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$Username,
[ValidateNotNullOrEmpty()]
[string]$Password,
[ValidateNotNullOrEmpty()]
[long]$PageId,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
$Data
)
[Net.ServicePointManager]::SecurityProtocol = 'Tls12, Tls11'
$Encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$($Username):$Password"))
$Header = #{"Authorization" = "Basic $Encoded"
}
Write-Information ($Data | Out-String)
$Data.GetType()
# Updates the Wiki page
Invoke-RestMethod "$($Server)/rest/api/content/$($PageId)" -Method PUT -Headers $Header -ContentType "application/json" -Body $Data -PreserveAuthorizationOnRedirect
}
As you can see I have get a print out of the JSon object as part of the update function. This is the print out:
{
"version": {
"number": 9
},
"body": {
"storage": {
"value": "<p><table><tr><th>Vendor-Application</th><th>Version</th></tr></table></p>",
"representation": "storage"
}
},
"title": "Windows Build Agent Tool Set",
"type": "page"
}
And this is the powershell error I get:
VERBOSE: received -byte response of content type application/json
Invoke-RestMethod : {"statusCode":500,"message":"org.codehaus.jackson.JsonParseException: Unexpected character ('M' (code 77)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')\n at [Source: com.atlassian.confluence.plugins.restapi.filters.LimitingRequestFilter$1#6b6831ec; line: 1, column: 2]"}
At C:\Users\ChildsC\Documents\Git\PowerShellModules\Wiki\Update-WikiPage.ps1:65 char:2
+ Invoke-RestMethod "$($Server)/rest/api/content/$($PageId)" -Metho ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Method: PUT, Re...ication/json
}:HttpRequestMessage) [Invoke-RestMethod], HttpResponseException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
EDIT
I have noticed that he issue happens when I am passing the json object from the Create Json -> Deploy -> Update
if I create the json in the Deploy script and pass it to the Update it works without issues.
Ok
So for this it turns out I was an idiot. And no-one else would see the problem because I did not post the complete script.
Earlier in the Create function I had a line to post params to the console:
$PsBoundParameters | Out-String
So when I thought the script was returning only the $ToolsPage value it was actually also returning another object with it.
Therefore the reason why the Update Script is because I am explicitly passing on the $ToolsPage variable.
I found this out by setting Parameters, which should hold the value of the object to $Global:variable_name and then compared the values from my console.
I have a function mainFunction that gets 2 parameters - $name will be just a regular string, and $moveFunction will be some function.
I want to start a job of a ScriptBlock ($SB) that will invoke $moveFunction with $name as his argument.
function foo($a){
Write-Output "In function foo with the argument => $a"
}
$SB = {
param($C, $fooFunction)
$fooFunction.Invoke($C)
}
function mainFunction($name, $moveFunction){
Start-Job -Name "currentJob" -ArgumentList $name, ${Function:$moveFunction} -ScriptBlock $SB
}
$j1 = mainFunction -name "output!" -moveFunction $Function:foo
I checked that $moveFunction exists in mainFunction already ($moveFunction.invoke(5) at mainFunction)
I can't find the problem in passing the function as argument in the start-job.
and from Get-Job -Name "CurrentJob" | Receive-Job I get:
You cannot call a method on a null-valued expression.
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
+ PSComputerName : localhost
Any help would be appreciated.
edit:
The problem is most likely the way I pass the function as an argument (${Function:$moveFunction}
Just a rehash of my previous comment plus code example. Similar issue here. Essentially, arguments passed to Jobs and Remote commands are serialized. During the de-serialization process, functions and script blocks come out as strings instead of their original type. Fortunately it's a simple process to transform these into invokable scriptblocks using [ScriptBlock]::Create("string").
function foo {
write-host "foo"
}
function bar {
# This argument comes in as a string
param($func)
write-host "bar"
# Create scriptblock from string
$func = [ScriptBlock]::Create($func)
$func.invoke()
}
Start-Job -ArgumentList $Function:Foo -ScriptBlock $Function:Bar
Get-Job | Wait-job
Get-Job | Receive-job
You passing the same function and invoking it. You can directly use the function in the job.
Start-Job -Name "currentJob" -ArgumentList $name - ScriptBlock ${function:foo}
Why is the following code not working? According to this article the usage of global should be correct: http://technet.microsoft.com/en-us/library/ff730957.aspx
Function global:writeLog {
param($logType, $logString, $logFile)
$fileStream = New-Object IO.FileStream $logFile ,'Append','Write','Read'
$streamWriter = New-Object System.IO.StreamWriter $fileStream
$time = get-date -Format "hh:mm:ss"
$streamWriter.writeLine("[${time}][$logType] ${logString}")
$streamWriter.close()
}
$temp = {
writeLog -logType "INFO" -logString "Test" -logFile "d:\scripts\powershell\logtest.txt"
}
Start-Job -ScriptBlock $temp
get-job | receive-job -AutoRemoveJob -Wait
This is the exception that powershell throws
The term 'writeLog' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included,
verify that the path is correct and try again.
+ CategoryInfo : ObjectNotFound: (writeLog:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
+ PSComputerName : localhost
From the documentation of Start-Job:
A Windows PowerShell background job runs a command "in the background" without interacting with the current session.
Therefor, the current session scope is ignored.
Trivial Solution: Define the function inside the scriptblock.
$JobScript = {
function write-log {
....
}
write-log <parameters>
}
Alternatively, check these related questions:
Powershell: passing parameters to a job
Variables in Start-Job
PowerShell jobs actually run in a separate PowerShell process. You can see this like so:
$pid
Start-Job {$pid} | Receive-Job -Wait
Where $pid is the current PowerShell's process id.
Anything that needs to be accessed from the script that runs in the job, must be either defined in the scriptblock passed to Start-Job i.e. function defined in the script block or as parameters passed into the script block using the -ArgumentList parameter on Start-Job or the script can dot source another script (or import a module) that contains the functions it needs. Personally, I would put shared functions in a module like Utils.psm1 and then import like so:
Start-Job {param($scriptdir) Import-Module $scriptdir\Utils.psm1; ...} -Arg $PSScriptRoot
Define the function in a script block, then use
Invoke-Command with NoNewScope to get it in the current scope
The InitializationScript parameter to get it into the job
#Create Shared Functions Script Block
[scriptblock] $func = {function getPID() {write-output "Function Running in process id: $pid!"}}
#Set up functions in normal script scope so they're accessible here
Invoke-Command -NoNewScope -ScriptBlock $func
write-output "Main script started"
#run the function from the parent script
getPID
#Create background job script
$jobScript = {getPID}
#Run background job
write-output "starting background job"
Start-Job $jobScript -name "Job1" -InitializationScript $func
get-job | Receive-Job
get-job | Stop-Job
get-job | Remove-Job