What is the value of a powershell scriptblock? - powershell-remoting

I am trying to run a piece of remote code using powershell remoting and getting some strange behavior which I am unable to explain. This is the sequence of commands I run.
$sb1 = {$r1 = 1; $r2 = 2; $r3 = Get-Culture; return $r3}
$sb2 = {1; 2; $r3 = Get-Culture; return $r3}
$session = New-PSSession -ComputerName $comp -Credential $creds
$ret1 = Invoke-Command -Session $Session -ScriptBlock $sb1
$ret2 = Invoke-Command -Session $Session -ScriptBlock $sb2
$ret1
>>> en-US
$ret2
>>> 1
Does anyone know a reason for this behavior? I find it very odd. The return statement is ignored, and the scriptblock is evaluated to the first 'uncaptured' expression. Hmmm?
Also, if I did want this block to always evaluate to the return statement, or even the last statement, does anyone know how I might accomplish that?

The entire script block is executed and the results are returned. $ret2 will contain three answers. The first is "1", the second is "2" and the third is the output of Get-Culture. You can explore these by looking at $ret2[0], $ret2[1], and $ret[2]. You can find out how many results are returned with $ret2.count.
Below shows everything in $ret2 on my computer.
PS C:\Users\user\Documents\PowerShell> $ret2 | select * | fl
#{PSComputerName=MyComputer; RunspaceId=b9568f5d-88a0-4346-be1a-827b8ba2f29d; PSShowComputerName=True}
#{PSComputerName=MyComputer; RunspaceId=b9568f5d-88a0-4346-be1a-827b8ba2f29d; PSShowComputerName=True}
PSComputerName : MyComputer
RunspaceId : b9568f5d-88a0-4346-be1a-827b8ba2f29d
Parent : en
LCID : 1033
KeyboardLayoutId : 1033
Name : en-US
IetfLanguageTag : en-US
DisplayName : English (United States)
NativeName : English (United States)
EnglishName : English (United States)
TwoLetterISOLanguageName : en
ThreeLetterISOLanguageName : eng
ThreeLetterWindowsLanguageName : ENU
CompareInfo : CompareInfo - en-US
TextInfo : TextInfo - en-US
IsNeutralCulture : False
CultureTypes : SpecificCultures, InstalledWin32Cultures, FrameworkCultures
NumberFormat : System.Globalization.NumberFormatInfo
DateTimeFormat : System.Globalization.DateTimeFormatInfo
Calendar : System.Globalization.GregorianCalendar
OptionalCalendars : {System.Globalization.GregorianCalendar, System.Globalization.GregorianCalendar}
UseUserOverride : True
IsReadOnly : False

Related

Post message to MS Teams via powershell

This is my first post here ,i hope i will get some help.
I have created a script in powershell for checking the expiry date of services principal in azure.The script itself works fine but now i want to post the output of script to MS Teams instead of generating a result file.
This is a piece of code.The result.list contains the output and i am loading it to $Body
$Body = get-content -Path .\result.list
$JSONBody = [PSCustomObject][Ordered]#{
"#type" = "text/plain"
"#context" = "http://schema.org/extensions"
"summary" = "Incoming Alert Test Message!"
"themeColor" = '0078D7'
"title" = "Incoming Alert Test Message!"
"text" = "$Body"
}
$TeamMessageBody = ConvertTo-Json $JSONBody -Depth 100
$parameters = #{
"URI" = '<uri>'
"Body" = $TeamMessageBody
"ContentType" = 'application/json'
}
Invoke-RestMethod #parameters
This i how the result.list looks like.E.g for a service-principal
DisplayName : sp-acr-ldl-pull
ObjectId : ***********
ApplicationId : ***********
KeyId : ***********
Type : Password
StartDate : 6/23/2020 2:29:20 PM
EndDate : 6/23/2021 2:29:16 PM
Status : Expired
But in Teams it is not human readable because the body is not passed correctly
DisplayName: sp-acr-ldl-pull ObjectId: ********** ApplicationId : ********** KeyId: *********** Type: Password StartDate: 6/23/2020 2:29:20PM EndDate
How can i format $JSONBody to get the same output in Teams same in result.list?
Basically, the problem is the line breaks, but you've got two related problems:
You're reading the contents of the file directly using Get-Content. By default in PowerShell this will return not a single string but rather an array of strings, one item for each line in the file. To get the contents as a single raw string, add the -Raw parameter at the end of the command.
By default, Teams is simply displaying your text without any formatting (including line breaks). If you want to send explicit line breaks, you need to tell Teams to do so, and you can do this using html "" tags (Teams message bodies support a limited subset of html). To do this, you can replace the line breaks in the $body variable with html <br> tags, like this: $Body = $Body.Replace("`r`n", "<br>").
Note that you're missing the "Method" parameter on your Invoke-RestMethod command.
Here is final working code, including the above:
$Body = get-content -Path "C:\temp\result.list" -Raw
$Body = $Body.Replace("`r`n", "<br>")
$JSONBody = [PSCustomObject][Ordered]#{
"#type" = "text/plain"
"#context" = "http://schema.org/extensions"
"summary" = "Incoming Alert Test Message!"
"themeColor" = '0078D7'
"title" = "Incoming Alert Test Message!"
"text" = "$Body"
}
$TeamMessageBody = ConvertTo-Json $JSONBody -Depth 100
$parameters = #{
"URI" = '<uri>'
"Body" = $TeamMessageBody
"ContentType" = 'application/json'
"Method" = "POST"
}
Invoke-RestMethod #parameters
So that will at least give you line breaks. If you want to preserve the "Table" style formatting, then I'd suggest using Adaptive Cards rather, perhaps something like the FactSet. You can learn how to send Adaptive Cards here, and here is a sample card using a FactSet, but you'd need to loop through the lines and build up the FactSet and the Adaptive Card, so hopefully the earlier solution is sufficient.

In a JSON array, how to search for a certain record returned by ConvertFrom-Json?

With Powershell 6 I am able to download a file in JSON format with:
PS C:\> Invoke-WebRequest https://login.microsoftonline.com/common/discovery/v2.0/keys |
>> ConvertFrom-Json |
>> Select-Object -expand keys
kty : RSA
use : sig
kid : CtTuhMJmD5M7DLdzD2v2x3QKSRY
x5t : CtTuhMJmD5M7DLdzD2v2x3QKSRY
n : 18uZ3P3IgOySlnOsxeIN5WUKzvlm6evPDMFbmXPtTF0GMe7tD2JPfai2UGn74s7AFwqxWO5DQZRu6VfQUux8uMR4J7nxm1Kf__7pVEVJJyDuL5a8PARRYQtH68w-0IZxcFOkgsSdhtIzPQ2jj4mmRzWXIwh8M_8pJ6qiOjvjF9bhEq0CC_f27BnljPaFn8hxY69pCoxenWWqFcsUhFZvCMthhRubAbBilDr74KaXS5xCgySBhPzwekD9_NdCUu
Csdqavd4T-VWnbplbB8YsC-R00FptBFKuTyT9zoGZjWZilQVmj7v3k8jXqYB2nWKgTAfwjmiyKz78FHkaE-nCIDw
e : AQAB
x5c : {MIIDBTCCAe2gAwIBAgIQXVogj9BAf49IpuOSIvztNDANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTIwMDMxNzAwMDAwMFoXDTI1MDMxNzAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBB
QADggEPADCCAQoCggEBANfLmdz9yIDskpZzrMXiDeVlCs75ZunrzwzBW5lz7UxdBjHu7Q9iT32otlBp++LOwBcKsVjuQ0GUbulX0FLsfLjEeCe58ZtSn//+6VRFSScg7i+WvDwEUWELR+vMPtCGcXBTpILEnYbSMz0No4+Jpkc1lyMIfDP/KSeqojo74xfW4RKtAgv39uwZ5Yz2hZ/IcWOvaQqMXp1lqhXLFIRWbwjLYYUbmwGwYpQ6++Cml0u
cQoMkgYT88HpA/fzXQlLgrHamr3eE/lVp26ZWwfGLAvkdNBabQRSrk8k/c6BmY1mYpUFZo+795PI16mAdp1ioEwH8I5osis+/BR5GhPpwiA8CAwEAAaMhMB8wHQYDVR0OBBYEFF8MDGklOGhGNVJvsHHRCaqtzexcMA0GCSqGSIb3DQEBCwUAA4IBAQCKkegw/mdpCVl1lOpgU4G9RT+1gtcPqZK9kpimuDggSJju6KUQlOCi5/lIH5DCzpjFd
mG17TjWVBNve5kowmrhLzovY0Ykk7+6hYTBK8dNNSmd4SK7zY++0aDIuOzHP2Cur+kgFC0gez50tPzotLDtMmp40gknXuzltwJfezNSw3gLgljDsGGcDIXK3qLSYh44qSuRGwulcN2EJUZBI9tIxoODpaWHIN8+z2uZvf8JBYFjA3+n9FRQn51X16CTcjq4QRTbNVpgVuQuyaYnEtx0ZnDvguB3RjGSPIXTRBkLl2x7e8/6uAZ6tchw8rhcOtP
sFgJuoJokGjvcUSR/6Eqd}
issuer : https://login.microsoftonline.com/{tenantid}/v2.0
kty : RSA
use : sig
kid : SsZsBNhZcF3Q9S4trpQBTByNRRI
x5t : SsZsBNhZcF3Q9S4trpQBTByNRRI
n : uHPewhg4WC3eLVPkEFlj7RDtaKYWXCI5G-LPVzsMKOuIu7qQQbeytIA6P6HT9_iIRt8zNQvuw4P9vbNjgUCpI6vfZGsjk3XuCVoB_bAIhvuBcQh9ePH2yEwS5reR-NrG1PsqzobnZZuigKCoDmuOb_UDx1DiVyNCbMBlEG7UzTQwLf5NP6HaRHx027URJeZvPAWY7zjHlSOuKoS_d1yUveaBFIgZqPWLCg44ck4gvik45HsNVWT9zYfT74dvUS
SrMSR-SHFT7Hy1XjbVXpHJHNNAXpPoGoWXTuc0BxMsB4cqjfJqoftFGOG4x32vEzakArLPxAKwGvkvu0jToAyvSQ
e : AQAB
x5c : {MIIDBTCCAe2gAwIBAgIQWHw7h/Ysh6hPcXpnrJ0N8DANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTIwMDQyNzAwMDAwMFoXDTI1MDQyNzAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBB
QADggEPADCCAQoCggEBALhz3sIYOFgt3i1T5BBZY+0Q7WimFlwiORviz1c7DCjriLu6kEG3srSAOj+h0/f4iEbfMzUL7sOD/b2zY4FAqSOr32RrI5N17glaAf2wCIb7gXEIfXjx9shMEua3kfjaxtT7Ks6G52WbooCgqA5rjm/1A8dQ4lcjQmzAZRBu1M00MC3+TT+h2kR8dNu1ESXmbzwFmO84x5UjriqEv3dclL3mgRSIGaj1iwoOOHJOIL4
pOOR7DVVk/c2H0++Hb1EkqzEkfkhxU+x8tV421V6RyRzTQF6T6BqFl07nNAcTLAeHKo3yaqH7RRjhuMd9rxM2pAKyz8QCsBr5L7tI06AMr0kCAwEAAaMhMB8wHQYDVR0OBBYEFOI7M+DDFMlP7Ac3aomPnWo1QL1SMA0GCSqGSIb3DQEBCwUAA4IBAQBv+8rBiDY8sZDBoUDYwFQM74QjqCmgNQfv5B0Vjwg20HinERjQeH24uAWzyhWN9++Fm
eY4zcRXDY5UNmB0nJz7UGlprA9s7voQ0Lkyiud0DO072RPBg38LmmrqoBsLb3MB9MZ2CGBaHftUHfpdTvrgmXSP0IJn7mCUq27g+hFk7n/MLbN1k8JswEODIgdMRvGqN+mnrPKkviWmcVAZccsWfcmS1pKwXqICTKzd6WmVdz+cL7ZSd9I2X0pY4oRwauoE2bS95vrXljCYgLArI3XB2QcnglDDBRYu3Z3aIJb26PTIyhkVKT7xaXhXl4Ogrbm
Qon9/O61G2dzpjzzBPqNP}
issuer : https://login.microsoftonline.com/{tenantid}/v2.0
(and there are more records in the keys array...)
How could I please search for the record with kid = "SsZsBNhZcF3Q9S4trpQBTByNRRI" and then return its x5c property?
Is the following a good way to find the record (I am confused by -expand, does it strip the outside JSON object and return just the keys array?) and then how to access its x5c property please?
PS C:\> Invoke-WebRequest https://login.microsoftonline.com/common/discovery/v2.0/keys |
>> ConvertFrom-Json |
>> Select-Object -expand keys |
>> Where-Object -Property kid -Eq SsZsBNhZcF3Q9S4trpQBTByNRRI
kty : RSA
use : sig
kid : SsZsBNhZcF3Q9S4trpQBTByNRRI
x5t : SsZsBNhZcF3Q9S4trpQBTByNRRI
n : uHPewhg4WC3eLVPkEFlj7RDtaKYWXCI5G-LPVzsMKOuIu7qQQbeytIA6P6HT9_iIRt8zNQvuw4P9vbNjgUCpI6vfZGsjk3XuCVoB_bAIhvuBcQh9ePH2yEwS5reR-NrG1PsqzobnZZuigKCoDmuOb_UDx1DiVyNCbMBlEG7UzTQwLf5NP6HaRHx027URJeZvPAWY7zjHlSOuKoS_d1yUveaBFIgZqPWLCg44ck4gvik45HsNVWT9zYfT74dvUS
SrMSR-SHFT7Hy1XjbVXpHJHNNAXpPoGoWXTuc0BxMsB4cqjfJqoftFGOG4x32vEzakArLPxAKwGvkvu0jToAyvSQ
e : AQAB
x5c : {MIIDBTCCAe2gAwIBAgIQWHw7h/Ysh6hPcXpnrJ0N8DANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTIwMDQyNzAwMDAwMFoXDTI1MDQyNzAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBB
QADggEPADCCAQoCggEBALhz3sIYOFgt3i1T5BBZY+0Q7WimFlwiORviz1c7DCjriLu6kEG3srSAOj+h0/f4iEbfMzUL7sOD/b2zY4FAqSOr32RrI5N17glaAf2wCIb7gXEIfXjx9shMEua3kfjaxtT7Ks6G52WbooCgqA5rjm/1A8dQ4lcjQmzAZRBu1M00MC3+TT+h2kR8dNu1ESXmbzwFmO84x5UjriqEv3dclL3mgRSIGaj1iwoOOHJOIL4
pOOR7DVVk/c2H0++Hb1EkqzEkfkhxU+x8tV421V6RyRzTQF6T6BqFl07nNAcTLAeHKo3yaqH7RRjhuMd9rxM2pAKyz8QCsBr5L7tI06AMr0kCAwEAAaMhMB8wHQYDVR0OBBYEFOI7M+DDFMlP7Ac3aomPnWo1QL1SMA0GCSqGSIb3DQEBCwUAA4IBAQBv+8rBiDY8sZDBoUDYwFQM74QjqCmgNQfv5B0Vjwg20HinERjQeH24uAWzyhWN9++Fm
eY4zcRXDY5UNmB0nJz7UGlprA9s7voQ0Lkyiud0DO072RPBg38LmmrqoBsLb3MB9MZ2CGBaHftUHfpdTvrgmXSP0IJn7mCUq27g+hFk7n/MLbN1k8JswEODIgdMRvGqN+mnrPKkviWmcVAZccsWfcmS1pKwXqICTKzd6WmVdz+cL7ZSd9I2X0pY4oRwauoE2bS95vrXljCYgLArI3XB2QcnglDDBRYu3Z3aIJb26PTIyhkVKT7xaXhXl4Ogrbm
Qon9/O61G2dzpjzzBPqNP}
issuer : https://login.microsoftonline.com/{tenantid}/v2.0
Store the output of the function in a variable like:
$var = Invoke-WebRequest https://login.microsoftonline.com/common/discovery/v2.0/keys | ConvertFrom-Json
then take the "keys" and "x5c" part of the variable with:
$var.keys; $var.x5c
This is everyday powershell. Pipe to that. It looks like x5c is an array too.
| select -expand x5c

Trim output from form to HTML table to not show form information or extra string info

I have a command:
$global:cpus+=$m.Trim("Name")
the results are:
> Microsoft Windows 10
> Enterprise|C:\WINDOWS|\Device\Harddisk0\Partition4
I would like to get rid of everything after Enterprise
Another example:
$global:domain+=$dmn.Trim()
the results are:
> Note if on domain: HostName : XXXXX DomainName : XXXXX.com NodeType :
> Hybrid DhcpScopeName : IsWinsProxy : False
I'd like for it just to show the domain name. How can I do it?
For the first one, split on |, then grab only the first part:
'Microsoft Windows 10 Enterprise|C:\WINDOWS|\Device\Harddisk0\Partition4'.Split('|')[0]
For the second one, use the -match regex operator to capture the domain name, then extract it from the $Matches variable:
$string = "Note if on domain: HostName : XXXXX DomainName : XXXXX.com NodeType : Hybrid DhcpScopeName : IsWinsProxy : False"
if($string -match 'DomainName : ([\w\.]+)\s+'){
$domain = $Matches[1]
}

How can I programmatically determine the default value of a function parameter?

Consider the following function:
function f {param($x = 42)}
$x has a default value of 42. Suppose I have a bunch of functions whose parameters I want to test programmatically for, among other things, their default values. Those other things I am able to discover using the objects returned using one of the following commands:
Get-Item function:/f | % Parameters | % x | % Attributes
Get-Help f | % Parameters | % parameter
Those commands output the following:
Position : 0
ParameterSetName : __AllParameterSets
Mandatory : False
ValueFromPipeline : False
ValueFromPipelineByPropertyName : False
ValueFromRemainingArguments : False
HelpMessage :
HelpMessageBaseName :
HelpMessageResourceId :
DontShow : False
TypeId : System.Management.Automation.ParameterAttribute
name : x
required : false
pipelineInput : false
isDynamic : false
parameterSetName : (All)
parameterValue : Object
type : #{name=Object}
position : 0
aliases : None
There doesn't seem to be any clue as to the default value.
How can I programmatically determine the default value of a function parameter?
You can use syntax tree to find default value expression for parameter.
function f {
param(
$x = 42,
$y = 6*7,
$z = (Get-Random)
)
}
$Parameters = (Get-Item function:\f).ScriptBlock.Ast.Body.ParamBlock.Parameters
$xDefaultValue = $($Parameters.Where{$_.Name.VariablePath.UserPath -eq 'x'}).DefaultValue
$yDefaultValue = $($Parameters.Where{$_.Name.VariablePath.UserPath -eq 'y'}).DefaultValue
$zDefaultValue = $($Parameters.Where{$_.Name.VariablePath.UserPath -eq 'z'}).DefaultValue
You can than use SafeGetValue() method of syntax tree node to retrieve constant value, but it does not work with expressions.
$xDefaultValue.SafeGetValue()
$yDefaultValue.SafeGetValue()
$zDefaultValue.SafeGetValue()

PowerShell parameters from file

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 #_
}