The Question
Does anyone know of a method to get the same data as $MyInvocation? Strong preference is using only built in stuff and not 3rd party add on software to accomplish this.
The Problem
When you create a session with -NoMachineProfile $MyInvocation is not populated. I was hoping there was some other way to get the information just as there is with [System.Environment]::Username is for $env:USERNAME.
I am not able to change the way things are invoked. Please do not suggest removing -NoMachineProfile as a solution since that is not available as an option.
I do not want to rely on $args because that is post-processing. Namely quotes, variables and other passed arguments are no longer identical to what they were when they were passed.
Background
$MyInvocation.line contains the exact command with 0 parsing provided.
e.g.
SomeScript.ps1
write-host ("Invoked line: "+$MyInvocation.line)
.\somescript.ps1 foo bar "foo and bar"
yields the following output
Invoked line: .\somescript.ps1 foo bar "foo and bar"
quotes and all, it means it is capable of being cut and pasted and exactly run the same way with 0 user translation.
-NoMachineProfile invocation
$SE_ADMIN_CREDENTIALS = Get-Credential
$PS_SESSION_OPTIONS = New-PSSessionOption -NoMachineProfile
$arrayRemoteTarget = "thehostname"
$strScriptToRun = ".\somescript.ps1"
$StrArguments = "arg1,arg2,arg3"
$icmCommand = "Invoke-Command -Credential `$SE_ADMIN_CREDENTIALS -SessionOption `$PS_SESSION_OPTIONS -ComputerName `$arrayRemoteTarget -FilePath `$strScriptToRun -ArgumentList (`$strArguments).Split("","")"
Invoke-Expression $icmCommand
-sessionoption (new-pssessionoption -nomachineprofile)
is another way to do that.
As for passing arguments, i believe
$using:variable
is what you're looking for
e.g.
$listing = "*"
invoke-command -computername $computer -scriptblock {cd /; ls $using:listing}
i hope that makes sense.
otherwise you could use "arg[0]" or "args[0]" in the file to run script... should work...
One final note, your quotes in quotes, is likely messing up your script, i'd just replace split("","") with split(',')
Related
I'd like to setup a cmdlet to start and stop mysql, and I'm trying to do so with Start-Job. the I've got the following in my Powershell profile:
$mysqlpath = "C:\Program Files\MySQL\MySQL Server 5.5\bin"
Function Start-Mysql
{
Start-Job -ScriptBlock { & "$mysqlpath\mysqld.exe" }
}
The variable doesn't seem to be expanding in the job command however? I must be missing some sort of scoping rule. Could someone please advise? Thanks!
you have to use the -argumentlist parameter see get-help start-job :
start-job -ScriptBlock { & $args[0] } -ArgumentList #($mysqlpath )
note that in V3 you just have to use the prefix using: before your varname ex:
Start-Job -ScriptBlock { & "$using:mysqlpath\mysqld.exe" }
Loïc MICHEL's answer is correct, but if you find it becomes difficult to deal with remembering which positional argument is which within the ScriptBlock, I'd like to offer a trick using the param keyword. Within the ScriptBlock, begin the body with param like you would for an advanced function, and put your code after also as if it were a function:
Note: The ScriptBlock param name does not need to be the same in the ScriptBlock and current session, it can be the same or something totally different. The important thing is you match the correct argument positionally in the -ArgumentList.
Start-Job { param( $mysqlpath ) & "$mysqlpath\mysqld.exe" } -ArgumentList $mysqlpath
This works because a ScriptBlock is just an unnamed function, so you can define parameters in mostly the same way you can when defining a proper function. The arguments in -ArgumentList are passed to the ScriptBlock as positional arguments in the order provided, so by default the order the arguments are passed is the same order they will be bound to named parameters in.
While the $using: scope is syntactically easier to work with, this method gets you the best of all worlds here, as the $using: scope cannot be used within the current session. This is incredibly useful you have a ScriptBlock that needs to be able to execute in any context and it's complicated enough that referencing the arguments by index becomes difficult to manage. This approach allows you to name your parameters and works with any ScriptBlock in all execution contexts, whether it's Start-Job, Invoke-Command, powershell.exe, or executing a ScriptBlock with the call operator &.
This is one of (if not the) most portable solution if you want to use named variables instead of referencing $args[i] for every variable.
I know that a script block executed via start-job cannot see the variables outside of the script block. To pass variables in you use the -arguments paramater. So why does this work (from MS article):
Start-Job -ScriptBlock { Get-Process -Name $args } -ArgumentList "powershell"
But this does not:
Start-Job -ScriptBlock { Get-aduser $args } -ArgumentList "samaccountname"
When I run it and receive the job I get the following error:
Cannot convert 'samaccountname' to the type 'Microsoft.ActiveDirectory.Management.ADUser' required by parameter 'Identity'. Specified method is not supported.
This same syntax works though outside of running it via start-job:
Get-aduser "samaccountname"
This last command is to demonstrate to you that the syntax is correct in the start-job script block. So why does the command expect an ADUser object when executed via start-job wheras outside of the script block it will accept a string?
I need to be able to execute the command via start-job
All credit here goes to Abraham in the comments above. He provided the answer but for whatever reason didn't submit it as an answer, so I am providing this to help others.
The issue is that -Arguments is always an array, even if you only provide one value. In my example where it didn't work with the second command (the get-Aduser one) is because I was passing an array to a paramater which expected a string. Changing the code to args[0] fixed this.
The first command worked because it's paramater accepts a string or an array.
NOTE: Passing complex objects to Start-job is a different story and has it's own issues, I posed this as a different question here
I am trying to start an installation via msiexec.
The following parameters are available and if I use this on that way, the installation will be correct.
This is the example from the vendor:
msiexec.exe /i ApexOne.msi MyServer="win2016-x64:8080|4343" MyDomain="Workgroup\Subdomain" /Lv+ "c:\temp\MSI.log"
This is my code:
Start-Process -FilePath "$env:systemroot\system32\msiexec.exe" -ArgumentList #('/i ".\agent.msi" MyServer="servername:8080|4344" MyDomain="999-Test"') -wait
But for automation I have to use the variable $domain to generate the correct domain.
I tried different codes, but nothing helped:
This is the code for my variable $domain
$domain='999-Test'
Example:
Start-Process -FilePath "$env:systemroot\system32\msiexec.exe" -ArgumentList #('/i ".\agent.msi" MyServer="servername:8080|4344" MyDomain=**"$domain"'**) -wait
I tried that also, but I don't know how to fix this failure.
I think the interpration of the variable is false, but I can not fix it.
-ArgumentList #('/i ".\agent.msi"','MyServer="servername:8080|4344"','MyDomain='$domain'')
Maybe someone can help me to fix this error?
Due to a long-standing bug in Start-Process, it is actually preferable to always pass all arguments for the target process encoded in a single string passed to -ArgumentList (-Args), rather than individually, as elements of an array - see this answer.
Doing so requires two things:
To incorporate variable values, you must use an expandable string, i.e. a double-quoted string, "...".
Any embedded quoting inside this overall "..." string must itself use double-quoting only, as CLIs on Windows generally only understand " (double quotes) and not also ' (single quotes) to have syntactic function. To embed " characters in a "..." string, escape them as `" (or as ""; inside a '...' string, you needn't escape them at all).
While this is somewhat cumbersome, it does have the advantage of allowing you to directly control the exact command line that the target process will see - and that enables you satisfy msiexec's picky syntax requirements where PROP="value with spaces" must be passed exactly with this value-only double-quoting (whereas PowerShell - justifiably - turns this argument into "PROP=value with spaces" behind the scenes, when it - of necessity - re-quotes arguments to synthesize the actual command line to use).
Therefore, use the following (using positional argument binding to -FilePath and -ArgumentList, respectively):
Start-Process -Wait $env:systemroot\system32\msiexec.exe "/i .\agent.msi MyServer=`"servername:8080|4344`" MyDomain=`"999-Test`""
Note: Strictly speaking, you do not need the embedded `"...`" quoting in your case, given that your property values have no embedded spaces.
The msiexec.exe program seems to have some peculiarities in its command-line parser. I recommend an approach like the following:
$msiFileName = ".\agent.msi"
$myServer = "servername:8080|4344"
$myDomain = "999-Test"
$argumentList = #(
'/i'
'"{0}"' -f $msiFileName
'MyServer="{0}"' -f $myServer
'MyDomain="{0}"' -f $myDomain
)
$startArgs = #{
"FilePath" = "msiexec.exe"
"ArgumentList" = $argumentList
"Wait" = $true
}
Start-Process #startArgs
This example creates an array of string arguments for the -ArgumentList parameter using single quotes (') and string formatting (-f). In the above example, each element in the $argumentList array is a separate string and contains the following elements:
/i
".\agent.msi"
MyServer="servername:8080|4344"
MyDomain="999-Test"
(Note that in the content of the array we are preserving the " characters.)
Next, we create a hashtable for the Start-Process cmdlet and call it using the # operator. (Creating a hashtable for a cmdlet's parameters is known as splatting and is a convenient way to build a large number of parameters for a cmdlet in a more readable and maintainable way.)
I am trying to use the invoke (ii) command to open an access database that has command line options. What I would like to have executed is below (yes there is a space in the name of the access database). The database is in the same folder as the Powershell script.
What I want: program name.accdb /cmd Rester
What I get: program name.accdb \cmd Rester
The exact commands I am using are:
$Path_To_EXE = "program name.accdb /cmd Rester"
&ii $Path_To_EXE
I am new to Powershell and have done some searching but can't seem to find an answer. I can create a work around by creating a separate .bat file but that seems like going backwards.
Thoughts?
You should also give a shot to the start-process cmdlet :
$Path_To_EXE = "c:\program.exe"
#Notice the simple quotes ...
$Arguments = #( "name.accdb", '/cmd' , "Rester" )
start-process -FilePath $Path_To_EXE -ArgumentList $Arguments -Wait
I'm not quite sure of the format of the answer you'll get tough ...
for database interaction, I'll rather use JGreenwell's Approach, since the answer that you'll get will be much easier to read/debug ...
Let me know if it works.
If you want to run a VBA script while passing it a parameter with powershell:
$aApp = New-Object -ComObject access.application
$aApp.Application.OpenCurrentDatabase("some program.accdb")
$aApp.Application.Run("VBAScriptName", [ref] "Raster")
First according to Microsoft Support you can use ;; for /cmd from the command line. Second because of the way call quotes and dequotes variables you have to include the /cmd flag separate from the variable (well, its the easiest way). Third, you might consider creating a new com-object to handle running Access with Powershell as it allows for a lot more options (just ask and I can add some examples of this). This being said try:
$Path_To_EXE = "program name.accdb"
&ii $Path_To_EXE ;;Rester #Try ;;"Rester" if it doesn't work.
#if that works then its a problem in Rester
#fyi another way is:
$Path_To_EXE = #("program name.accdb", ";;Rester")
&ii $Path_To_EXE
If you want to use an ActiveX Object Controller to open and perform operations on Access look at this blog from technet <- Read the link there are pitfalls to avoid.
$adOpenStatic = 3
$adLockOptimistic = 3
$objConnection = New-Object -com "ADODB.Connection"
$objRecordSet = New-Object -com "ADODB.Recordset"
$objConnection.Open("Provider = Microsoft.Jet.OLEDB.4.0; Data Source = C:\Scripts\Test.mdb")
$objRecordset.Open("Select * From Computers", $objConnection,$adOpenStatic,$adLockOptimistic)
$objRecordSet.AddNew()
$objRecordSet.Fields.Item("ComputerName").Value = "atl-ws-001"
$objRecordSet.Fields.Item("SerialNumber").Value = "192ATG43R"
$objRecordSet.Update()
$objRecordSet.Close()
$objConnection.Close()
I'd like to setup a cmdlet to start and stop mysql, and I'm trying to do so with Start-Job. the I've got the following in my Powershell profile:
$mysqlpath = "C:\Program Files\MySQL\MySQL Server 5.5\bin"
Function Start-Mysql
{
Start-Job -ScriptBlock { & "$mysqlpath\mysqld.exe" }
}
The variable doesn't seem to be expanding in the job command however? I must be missing some sort of scoping rule. Could someone please advise? Thanks!
you have to use the -argumentlist parameter see get-help start-job :
start-job -ScriptBlock { & $args[0] } -ArgumentList #($mysqlpath )
note that in V3 you just have to use the prefix using: before your varname ex:
Start-Job -ScriptBlock { & "$using:mysqlpath\mysqld.exe" }
Loïc MICHEL's answer is correct, but if you find it becomes difficult to deal with remembering which positional argument is which within the ScriptBlock, I'd like to offer a trick using the param keyword. Within the ScriptBlock, begin the body with param like you would for an advanced function, and put your code after also as if it were a function:
Note: The ScriptBlock param name does not need to be the same in the ScriptBlock and current session, it can be the same or something totally different. The important thing is you match the correct argument positionally in the -ArgumentList.
Start-Job { param( $mysqlpath ) & "$mysqlpath\mysqld.exe" } -ArgumentList $mysqlpath
This works because a ScriptBlock is just an unnamed function, so you can define parameters in mostly the same way you can when defining a proper function. The arguments in -ArgumentList are passed to the ScriptBlock as positional arguments in the order provided, so by default the order the arguments are passed is the same order they will be bound to named parameters in.
While the $using: scope is syntactically easier to work with, this method gets you the best of all worlds here, as the $using: scope cannot be used within the current session. This is incredibly useful you have a ScriptBlock that needs to be able to execute in any context and it's complicated enough that referencing the arguments by index becomes difficult to manage. This approach allows you to name your parameters and works with any ScriptBlock in all execution contexts, whether it's Start-Job, Invoke-Command, powershell.exe, or executing a ScriptBlock with the call operator &.
This is one of (if not the) most portable solution if you want to use named variables instead of referencing $args[i] for every variable.