I have the following code:
Function test {
Param (
[Parameter(Mandatory=$True, ValueFromPipeline=$True, ParameterSetName="p1", position=0)]
[string]$sFile,
[Parameter(Mandatory=$True, ValueFromPipeline=$True, ParameterSetName="p2", position=0)]
[int]$iFile,
[string]$Secret = "s3cr3t"
)
BEGIN {
"[BEGIN]Secret=${Secret}"
}
PROCESS {
"[PROCESS]Secret=${Secret}"
}
}
If i execute:
"file.txt" | test
I get:
[BEGIN]Secret=
[PROCESS]Secret=s3cr3t
My question is, why secret is empty in the begin block?
I am trying to perform some initialization actions in the begin block based on Secret and this behavior is stopping me.
Note that when i run test without pipeline input, the secret is initialized at BEGIN bock as well. Finally my powershell version is 2.
Related
I want a powershell script running as SYSTEM user to display a Windowsform on another users session and have interaction with the controls of it.
I am trying to automate the installation/repair of Symantec Endpoint Protection with Solarwinds N-Able. This platform uses agent software which is installed on clients to monitor and execute tasks on them.
The agent uses the NT AUTHORITY\SYSTEM user to execute tasks on the machine. The installation of SEP works fine so far, but the reboots in between the deinstall/install phases are still uncontrollable as a regular user on the machine. I want the currently active user be able to control this reboot cycles. Something like the Windows update reboot prompt.
My idea is to display a windowsform on logged on user's desktop with controls on it to execute or delay the reboot. My question now is how do I display a windowsform defined in powershell on another user's session, and how am I going to get the actions of the controls back in the script that is running on the SYSTEM user.
I've already tried the msg command to send a message to all the users on the system. But this is only one-way communication and isn't really meant to be used in situations like this is guess.
I found the solution for my problem. I used the WTSSendMessage function which boxdog suggested in the comments. I combined this with a script that gets the sessionID's of the logged on users. I minimized this script to only get the "Active" user's sessionID. This is then used to send the message to user. I tested it in Solarwinds and so far this works flawless.
My coding skills are pretty basic, but this is the end result.
function Send-MessageBox
{
[CmdletBinding()]
[OutputType([string])]
Param
(
[Parameter(Mandatory=$true, Position=0)]
[string]$title,
[Parameter(Mandatory=$true, Position=1)]
[string]$message,
[Parameter(Mandatory=$true, Position=2)]
[int]$duration,
[Parameter(Mandatory=$true, Position=3)]
[int]$style
)
Begin
{
$typeDefinition = #"
using System;
using System.Runtime.InteropServices;
public class WTSMessage {
[DllImport("wtsapi32.dll", SetLastError = true)]
public static extern bool WTSSendMessage(
IntPtr hServer,
[MarshalAs(UnmanagedType.I4)] int SessionId,
String pTitle,
[MarshalAs(UnmanagedType.U4)] int TitleLength,
String pMessage,
[MarshalAs(UnmanagedType.U4)] int MessageLength,
[MarshalAs(UnmanagedType.U4)] int Style,
[MarshalAs(UnmanagedType.U4)] int Timeout,
[MarshalAs(UnmanagedType.U4)] out int pResponse,
bool bWait
);
static int response = 0;
public static int SendMessage(int SessionID, String Title, String Message, int Timeout, int MessageBoxType) {
WTSSendMessage(IntPtr.Zero, SessionID, Title, Title.Length, Message, Message.Length, MessageBoxType, Timeout, out response, true);
return response;
}
}
"#
}
Process
{
if (-not ([System.Management.Automation.PSTypeName]'WTSMessage').Type)
{
Add-Type -TypeDefinition $typeDefinition
}
$RawOuput = (quser) -replace '\s{2,}', ',' | ConvertFrom-Csv
$sessionID = $null
Foreach ($session in $RawOuput) {
if(($session.sessionname -notlike "console") -AND ($session.sessionname -notlike "rdp-tcp*")) {
if($session.ID -eq "Active"){
$sessionID = $session.SESSIONNAME
}
}else{
if($session.STATE -eq "Active"){
$sessionID = $session.ID
}
}
}
$response = [WTSMessage]::SendMessage($sessionID, $title, $message, $duration, $style )
}
End
{
Return $response
}
}
Send-MessageBox -title "Title" -message "Message" -duration 60 -style 0x00001034L
I've got a release definition in vsts that needs access to a password I've defined as a secret variable in a variable group. I've linked that group to this release definition but when I run the task the parameter ends up blank. Is there anything special I need to do to get the value of a secret variable?
Definition of the powershell task that uses the password
Linked variable group
Error output:
2017-08-09T14:01:17.9262375Z ##[command]. 'C:\agent_work\r1\a\$(AGENT.BUILDDIRECTORY)\RCV\deploys\common\Get-FromArtifactory.ps1' -repoUsername developer -repoPassword -repoPath libs-snapshot-local
2017-08-09T14:01:18.8168538Z ##[error]C:\agent_work\r1\a\$(AGENT.BUILDDIRECTORY)\RCV\deploys\common\Get-FromArtifactory.ps1 : Missing an argument for parameter 'repoPassword'. Specify a parameter of type 'System.String' and try again.
Edited to add more info as requested.
This is the start of my Get-FromArtifactory.ps1
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[String]
$repoUsername,
[Parameter()]
[String]
$repoPassword,
# Other params
)
#setup credentials object for arty access
$secPassword = $repoPassword | ConvertTo-SecureString -asPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($repoUsername, $secPassword)
Edit 15/08/2017 - I've updated it to use quotes as suggested by #Marina-MSFT but it's still just passing in blank.
Amended script to print out part of password:
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[String]
$repoUsername,
[Parameter()]
[String]
$repoPassword,
#other params
)
write-host "pword $repoPassword"
write-host "1st char: $($repoPassword[0])"
2017-08-15T09:23:00.8606081Z ##[command]. 'C:\agent_work\r1\a\RCV\deploys\common\Get-FromArtifactory.ps1' -repoUsername developer -repoPassword "" -repoPath libs-snapshot-local -artifactName ipo-deposit-accounts_2.11 -artifactPath uk/gov/ipo -artifactVersion 0.1-SNAPSHOT -downloadDir C:\agent_work\r1\a
2017-08-15T09:23:02.3606174Z pword
2017-08-15T09:23:02.3606174Z 1st char:
Same with single quotes
2017-08-15T09:22:10.6573655Z ##[command]. 'C:\agent_work\r1\a\RCV\deploys\common\Get-FromArtifactory.ps1' -repoUsername developer -repoPassword '' -repoPath libs-snapshot-local -artifactName ipo-deposit-accounts_2.11 -artifactPath uk/gov/ipo -artifactVersion 0.1-SNAPSHOT -downloadDir C:\agent_work\r1\a
2017-08-15T09:22:11.2198723Z pword
2017-08-15T09:22:11.3761178Z 1st char:
I've got it working now, there were 2 things going on.
Having . in the name of a secret variable in a variable group
doesn't work. I recreated this issue in a separate release
definition/variable group.
Even after I removed the . in my var
names I still had to recreate the variable group for it to work.
Bug for issue with . in variable name raised here: https://developercommunity.visualstudio.com/content/problem/94475/secret-variable-in-variable-not-group-not-availabl.html
I am writing a PowerShell function which can take pipeline input. Specifically I am testing it with Import-CSV. Many of the params are not mandatory, which means sometimes the CSV will not have those columns. For boolean values this is working as expected, but with string values, a missing CSV field yields a copy of the row object in the string field.
Here is an example problem parameter:
[Parameter(Mandatory=$False,
ValueFromPipeline=$True,
ValueFromPipelinebyPropertyName=$True,
HelpMessage="TCP Port of the remote server")]
[Alias('Port', 'Remote Server Port')]
[string]$RemoteServerPort = "5500",
Now, if the field is missing, I would expect the value to be "5500" as specified, but instead I get:
$RemoteServerPort = #{Name=KG; IP=10.1.1.1; Username=Admin; Password=}
I've done some looking around, but frankly I'm not even sure what to search for.
This is because you specified ValueFromPipeline=$True so that PoSh coerces the piped object to a string if it cannot bind the parameter by property name. You could solve that by removing ValueFromPipeline=$True from this parameter and introduce another one to be bound to the piped object, i.e. something like this
function MyTestFunc() {
param(
[Parameter(ValueFromPipeline=$True)]
[object]$PipedObj,
[Parameter(Mandatory=$False,
ValueFromPipelinebyPropertyName=$True,
HelpMessage="TCP Port of the remote server")]
[Alias('Port', 'Remote Server Port')]
[string]$RemoteServerPort = "5500"
)
Write-Host "Port: $RemoteServerPort / Obj: $PipedObj"
}
$o1 = [pscustomobject]#{"Server" = "123"; "Port" = "12345"}
$o2 = [pscustomobject]#{"Server" = "1234"; "OS" = "Win3.1"}
$o1 | MyTestFunc
$o2 | MyTestFunc
Will result in
Port: 12345 / Obj: #{Server=123; Port=12345}
Port: 5500 / Obj: #{Server=1234; OS=Win3.1}
A way to see in detail what is actually happening behind the scenes is to use Trace-Command like so
Trace-Command ParameterBinding {$o2 | MyTestFunc} -PSHost
i am writing a code that runs on remote computer using psexec, in some point of the code it stop and wait for user to press enter in order to continue , this only happens on remote! when i use local its fine, how do i prevent that and keep my code "rolling"?
code:
param(
[Parameter(ParameterSetName='database')] [string]$database,
[Parameter(ParameterSetName='file')] [string]$file,
[Parameter(ParameterSetName='server')] [string]$server,
[Parameter(ParameterSetName='mailbox')] [string]$mailbox,
[Parameter(ParameterSetName='all')] [switch]$all,
[string]$filename
)
if($mailbox) { $mailboxes = #(Get-Mailbox $mailbox) } ----->#it stop after this function#
I'm calling a powershell script like so, from within another script.
function test_execute_sql_2_direct_query()
{
& C:\Server\Scripts\PowerShellDeploy\Execute-SQL-Command.ps1 -SERVER_NAME "myserver" -SQL_COMMAND_TYPE "input" -SQL_FILE ".\select.sql" -USER_NAME "username" -PASSWORD "password" -DATABASE "accounts"
}
When I call that function I get an error message from Powershell that says 'Cannot validate argument on parameter 'Query'. The argument is null or empty....'
I don't quite follow why I'm getting that message? The Execute-SQL-Command script doesn't have an argument called 'Query'. Quite possible I'm missing something.
Here's its paramenter section:
param(
[Parameter(
HelpMessage="Server Name")]
[string]$SERVER_NAME = "",
[Parameter(
HelpMessage="Database")]
[string]$DATABASE = "",
[Parameter(
HelpMessage="Path to .SQL File to run. Used in input file mode (not direct query mode)")]
[string]$SQL_FILE ="",
[Parameter(
HelpMessage="Server Port (default = 1433)")]
[string]$SERVER_PORT = "1433",
[Parameter(
HelpMessage="Server Transport (default = tcp")]
[string]$TRANSPORT = "tcp",
[Parameter(
HelpMessage="Method of connecting to database <passthrough, credentialed>")]
[string]$CONNECT_METHOD = "passthrough",
[Parameter(
HelpMessage="Username -- used only for credentialed login")]
[string]$USER_NAME = "username",
[Parameter(
HelpMessage="Command type-- direct query or input file (.sql)")]
[string]$SQL_COMMAND_TYPE = "DIRECT_QUERY",
[Parameter(
HelpMessage="SQL statement text type-- used in direct queries")]
[string]$SQL_STATEMENT = "",
[Parameter(
HelpMessage="Password -- used only for credentialed login")]
[string]$PASSWORD = "pwd")
This one is chalked up to user error-- the actual error was coming from within a line called in Execute-SQL-Command.ps1-- specifically, the Invoke-Sqlcmd cmdlet.