This question already has answers here:
How can you use an object's property in a double-quoted string?
(5 answers)
Closed 1 year ago.
I currently have an Azure DevOps pipeline that makes a REST API call which returns an object of parameters:
#{location=eastus2; envName=sandbox; ...}
My PowerShell script looks like:
steps:
- powershell: |
$uri = "https://dev.azure.com/{org}/{proj}/_apis/build/latest/{buildId}?branchName={name}&api-version=6.0-preview.1"
$token = [System.Text.Encoding]::UTF8.GetBytes("$(System.AccessToken)" + ":")
$base64 = [System.Convert]::ToBase64String($token)
$basicAuth = [string]::Format("Basic {0}", $base64)
$headers = #{ Authorization = $basicAuth }
$result = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -ContentType application/json
$params = $result.templateParameters
Write-Host "##vso[task.setvariable variable=paramObj]$params"
- powershell: |
Write-Host $(paramObj)
In this first PS task I can use $params.location which will return eastus2. However, in the second PS task I get an error saying:
Line |
2 | Write-Host #{location=eastus2; envName=Sandbox; sqlDBAutoPauseDatabas …
| ~~~~~~~
| The term 'eastus2' is not recognized as a name of a cmdlet,
| function, script file, or executable program. Check the
| spelling of the name, or if a path was included, verify that
| the path is correct and try again.
Since my API call has an object of parameters, I'm trying to pass that parameter object to another task where I can then use those values. In the second task seen above I can't log the result, so of course trying to use dot notation, or Select-Object all results in the same error.
I was also attempting to iterate over the parameters object so I could do something like:
steps:
- powershell: |
...
$params = $result.templateParameters
foreach ($param in $params) {
Write-Host "##vso[task.setvariable variable=test]$param.Value"
}
- powershell: |
Write-Host $(location)
But I haven't been able to figure this out either as I keep getting the same error listed above. How do I access the parameter values in a second script without hardcoding it?
You've encountered the magic of expandable string parsing in PowerShell.
When you place a variable expression, like $param, inside an expandable string (a string literal bounded with " double quotes), PowerShell will attempt to resolve it's value for you - but nothing more than that - it's not going to attempt to evaluate member invocation, like $param.Value, for example.
So a string literal like this:
"$param.Value"
Will expand to:
"#{location=eastus2;...}.Value"
Notice that $params was evaluated and it's value substituted in the string, and the .Value-part is completely ignored.
To force PowerShell to evaluate expressions more complicated than simple variable references, use the $() subexpression operator:
Write-Host "##vso[task.setvariable variable=test]$($param.Value)"
Related
I am trying to retrieve all projects from my organizations
I have 300 projects but api call returns only 100
I tried using $top with api then it returns nothing
Here is my script which works for getting 100 projects and converting to csv file but I need details of all 300 projects in this csv file -
connectionToken = ""
$BaseUrl = "https://dev.azure.com/{organization_name}/_apis/projects?api-
versions=5.0"
$base64AuthInfo =
[System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes
(":$($connectionToken)"))
$ProjectInfo = Invoke-RestMethod -Uri $BaseUrl -Headers #{authorization =
"Basic $base64AuthInfo"} - Method Get
$ProjectDetails = $ProjectInfo | ConvertTo-Json -Depth 100 | out-file
"/list.json"
$ProjectList = Get-Content "/list.json" -Raw | ConvertFromJson | select -
Expand value | Export-CSV "list.csv"
I also tried using invoke-webrequest it also returns nothing
Thank you
Azure devops rest api returns only 100 projects
The answer is use a specify $top parameter.
But when using this parameter in powershell, we need to be careful to escape the $ by ` instead of &.
That's because in powershell, single quotation '' marks only represent the characters within the quotation marks in any case. In other words, the content within single quotes will not be substituted for variables and escaped characters. In double quotes "", variable substitution and character escaping are allowed.
When you use the double quotes with the BaseUrl:
$BaseUrl = "https://dev.azure.com/{organization_name}/_apis/projects?api-versions=5.0"
We need escape character $ if we add $top=300 instead of & in that URL.
So, the BaseUrl should be:
$BaseUrl = "https://dev.azure.com/{organization_name}/_apis/projects?`$top=300&api-version=5.0"
Or you could use single quotation for the URL:
$BaseUrl = 'https://dev.azure.com/{organization_name}/_apis/projects?$top=300&api-version=5.0'
Check the document Escaping in PowerShell:
The PowerShell escape character is the backtick "`" character.
Below is my test result:
I'm using an Azure API > https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull%20request%20query/get?view=azure-devops-rest-5.1
I'm using this code:
$body = #"
{
"queries": [{
"items": [
"59c1c31397b266116ff6d735e5638ef5d1b598a0"
],
"type": "commit"
}]
}
"#
$x = Invoke-RestMethod -Uri $someLink -Headers #{Authorization = $pat } -Body $body -Method Post -ContentType 'application/json'
Write-Host $x
Write-Host $x | ConvertFrom-Json
Write-Host $x.results | ConvertFrom-Json
I've removed the link for security reasons.
When I run the code I get the following in my console:
2020-01-20T16:17:55.8600905Z #{queries=System.Object[]; results=System.Object[]}
2020-01-20T16:17:55.8637026Z #{queries=System.Object[]; results=System.Object[]}
2020-01-20T16:17:55.8674193Z
I'm not sure if the queries and results objects are empty or that I need some other way of reading them.
Write-Host writes to the host (which is typically the console / terminal), bypassing PowerShell's success output stream, which is why a command such as Write-Host $x | ConvertFrom-Json is pointless, because ConvertFrom-Json will receive no input.
Write-Host writes simple .ToString() stringifications of non-string input objects to the host, which means that you don't get PowerShell's rich, human-friendly output formatting that implicit output or - rarely needed - explicit Write-Output calls result in when ultimately printing to the host by default, due to the output neither getting captured in a variable or getting redirected with >).
Invoke-RestMethod automatically parses the JSON output it receives into PowerShell custom objects ([pscustomobject] instances, so there is also also no conceptual reason to pipe its property values to ConvertFrom-Json - unless the API truly returns nested JSON text as string values in the JSON it returns.
If you want to visually inspect the results, simply output them directly:
$x # output visualization of the whole object
$x.results # just the .results property
The above outputs to the success output stream, which means that an outside caller of your code would receive these values as part of the code's output.
If the resulting table- or list-like formatting (depending on the number of properties) doesn't tell you enough, you can convert the objects back to JSON, by piping to ConvertTo-Json.
Note that the latter's default serialization depth is limited to 2, so you may have to pass a higher -Depth value to see the object in full - see this post.
$x | ConvertTo-Json # use -Depth <n>, if needed.
$x.results | ConvertTo-Json
If you really want to just print the values as information for the user, without becoming part of the output, use Out-Host rather than Write-Host, because Out-Host does apply the rich formatting:
$x | Out-Host # print visualization of the whole object directly to the host
$x.results | Out-Host # just the .results property
# Ditto via ConvertTo-Json
$x | ConvertTo-Json | Out-Host
...
I am trying to pass a MAC Address and code (1,2,3,4) to Invoke-WebRequest.
Manually the command is working fine, but I am not able to do it via command.
The manual command that works is:
Invoke-WebRequest -Uri https://mywebsite.org/pcconfig/setstate.php?mac=F832E3A2503B"&"state=4
Now when I break this up into variable to use with the mac from the machine I do the following.
$LiveMAC = Get-NetAdapter -Physical |
where Status -eq "Up" |
Select-Object -ExpandProperty PermanentAddress
$Str1 = "https://mywebsite.org/pcconfig/setstate.php?mac=$LiveMAC"
$Str2 = $Str1 + '"&"' + 'state=4'
Invoke-WebRequest -Uri $Str2
When I run the above code I do not see errors in red, it seems to process but does not work.
Looking at $Str2 I see the below output, which seems correct, but when passing it as above it fails to work.
https://mywebsite.org/pcconfig/setstate.php?mac=F832E3A2503B"&"state=4
The double quotes in a statement like
Invoke-WebRequest -Uri https://example.org/some/sit.php?foo=x"&"bar=y
mask the ampersand for PowerShell, because otherwise otherwise PowerShell would throw an error that a bare & is reserved for future use. A more canonical way of avoiding this is to put the entire URI in quotes:
Invoke-WebRequest -Uri "https://example.org/some/sit.php?foo=x&bar=y"
Either way the actual URI that is passed to Invoke-WebRequest is
https://example.org/some/sit.php?foo=x&bar=y
without the quotes.
However, in a code snippet like this:
$Str1 = "https://example.org/some/site.php?foo=$foo"
$Str2 = $Str1 + '"&"' + 'state=4'
you're including literal double quotes as part of the URI, so the actual URI passed to the cmdlet would be
https://example.org/some/sit.php?foo=x"&"bar=y
which is invalid.
With that said, you don't need string concatenation for building your URI anyway. Simply put your variable in the double-quoted string:
$uri = "https://example.org/some/sit.php?foo=${foo}&bar=y"
If you need to insert the value of an object property or array element use either a subexpression
$uri = "https://example.org/some/sit.php?foo=$($foo[2])&bar=y"
or the format operator
$uri = 'https://example.org/some/sit.php?foo={0}&bar=y' -f $foo[2]
I have made a release in vsts with some environment variables.
One of those environment variables is as follows:
#Array
[ { "name":"password", "value":"thisismypassword" }, { ... } ]
However, I get an output parameter from one of the release tasks which returns the password. So I thought to make a 'tag' and replace it when the output parameter has returned:
[ { "name":"password", "value":"<Password>" } ]
When my output parameter has returned I can create an powershell task to replace the 'tag' with the real password. However to replace, it should be either an string or an valid powershell array. If I directly use the environment variable, it breaks on the first ':' with an error message (because it is not a legit powershell command/format);
#This breaks
$var = $(environment_variable)
Hence I thought to convert it to a String, replace it, Convert it back to json object and set it back on the environment variable:
$Setting = ConvertFrom-Json -InputObject '$(environment_variable)'
$Setting = $Setting -replace "<Password>", "$(Output_Password)"
#Tried both below
$Setting_JSON - ConvertTo-Json -InputObject $Setting
$Setting_JNSON = [Newtonsoft.Json.JsonConvert]::SerializeObject($Setting, [Newtonsoft.Json.Formatting]::None)
Write-Host "##vso[task.setvariable variable=$(environment_variable)]$Setting_JSON"
However these produce a json string which is of a different format and the step which uses this variable does not understand;
#Output
["#{name=Password;value=thisisapasswordvalue}"]
#Expected (and needed) Output
[ { "name":"password", "value":"thisisapasswordvalue" } ]
#This breaks
$var = $(environment_variable)
For this, you can use this:
$var = $Env:variablename
This works at my side:
$Setting = $env:Var1
$Setting = $Setting -replace "<Password>", "NewValue"
I have many oracle forms in one folder and I want to compile those forms through frmcmp command in powershell script.
I have written a powershell script which is following
$module="module="
get-childitem "C:\forms\fortest" -recurse |
where { $_.extension -eq ".fmb" } |
foreach {
C:\Oracle\Middleware\Oracle_FRHome1\BIN\frmcmp $module $_.FullName userid=xyz/xyz#xyz Output_File=C:\forms\11\common\fmx\$_.BaseName+'.fmx'
}
but this one is not working. i am new in powershell.
but when I try to compile a single form through command prompt its working like following.
frmcmp module=C:\forms\src\xyz.fmb userid=xyz/xyz#xyz Output_File=C:\forms\11\common\fmx\xyz.fmx
When you want to use variables inside a string in PowerShell you have different options. To start with, you will always need to use " as opposed to ' to wrap the string, if you want variables in your string.
$myVariable = "MyPropertyValue"
Write-Host "The variable has the value $MyVariable"
The above code would yield the output:
The variable has the value MyPropertyValue
If you want to use a property of a variable (or any expression) and insert it into the string, you need to wrap it in the string with $(expression goes here), e.g.
$MyVariable = New-Object PSObject -Property #{ MyPropertyName = 'MyPropertyValue' }
# The following will fail getting the property since it will only consider
# the variable name as code, not the dot or the property name. It will
# therefore ToString the object and append the literal string .MyPropertyName
Write-Host "Failed property value retrieval: $MyVariable.MyPropertyName"
# This will succeed, since it's wrapped as code.
Write-Host "Successful property value retrieval: $($MyVariable.MyPropertyName)"
# You can have any code in those wrappers, for example math.
Write-Host "Maths calculating: 3 * 27 = $( 3 * 27 )"
The above code would yield the following output:
Failed property value retrieval: #{MyPropertyName=MyPropertyValue}.MyPropertyName
Successful property value retrieval: MyPropertyValue
Maths calculating: 3 * 27 = 81
I generally try to use the Start-Process cmdlet when I start processes in PowerShell, since it gives me the possibility of additional control over the process started. This means that you could use something similar to the following.
Get-ChildItem "C:\forms\fortest" -Filter "*.fmb" -recurse | Foreach {
$FormPath = $_.FullName
$ResultingFileName = $_.BaseName
Start-Process -FilePath "C:\Oracle\Middleware\Oracle_FRHome1\BIN\frmcmp.exe" -ArgumentList "module=$FormPath", "userid=xyz/xyz#xyz", "Output_File=C:\forms\11\common\fmx\$ResultingFileName.fmx"
}
You could also add the -Wait parameter to the Start-Process command, if you want to wait with compilation of the next item until the current compilation has completed.