how to concatenate a network path and combobox variable in powershell - combobox

please help. I have a the following function.
PROCESS {
$ServersArray = #('localhost')
foreach ($serverArray in $ServersArray) {
try {
if ($WebConfig.SelectedIndex -gt -1) {
Write-Host -ForegroundColor Cyan "Applying Maintenance on $ServerArray"
$everything_ok = $true
Invoke-Command $serverArray -ScriptBlock {
$filePath = "D:\\Inetpub\\MyHL3Ordering\\Configuration\\MyHL" + "\\" + $WebConfig.SelectedItem
(Get-Content $filePath) | ForEach-Object {
$_ -replace 'allowDO="true"','allowDO="false"'
} | Set-Content $filePath -Encoding UTF8;
} -ErrorAction 'Stop'
}
so basically I would like to concatenate the path with the combobox selected item. for example. if the selected item is web_da-DK.config , the path should be
'D:\Inetpub\MyHL3Ordering\Configuration\MyHL\web_da-DK.config' but it is not working.
error is:
Cannot find part of the path 'D:\Inetpub\MyHL3Ordering\Configuration\MyHL\' it doesnt seem to concatenate the value of combobox selectedItem to the path.
Please let me know what am I doing wrong.

The problem is that you are trying to use a variable from a scope in which is does not exist. You can read more about scopes if you run the following command:
Get-Help about_scopes
Since you are using PowerShell v3 you can use the Using scope modifier. From the help on about_scopes:
The Using scope modifier
Using is a special scope modifier that identifies a local
variable in a remote command. By default, variables in remote
commands are assumed to be defined in the remote session.
The Using scope modifier is introduced in Windows PowerShell 3.0.
For more information, see about_Remote_Variables.
It further suggest reading the about_Remote_Variables, which states:
USING LOCAL VARIABLES
You can also use local variables in remote commands, but you must
indicate that the variable is defined in the local session.
Beginning in Windows PowerShell 3.0, you can use the Using scope
modifier to identify a local variable in a remote command.
The syntax of Using is as follows:
The syntax is:
$Using:<VariableName>
In order to take an example of this, we could make a sample first which tries to use the local variable immediately, like the following:
$serverArray = "localhost"
$filename = "somefile.txt"
Invoke-Command -ComputerName $ServerArray -ScriptBlock {
$concatenated = [System.IO.Path]::Combine("C:\rootpath", $filename)
Write-Host $concatenated
}
This will yield the following output:
C:\rootpath
If we change the script to use the Using scope modifier to indicate that we want to use a local variable from the remote scope, we get code like the following:
$serverArray = "localhost"
$filename = "somefile.txt"
Invoke-Command -ComputerName $ServerArray -ScriptBlock {
$concatenated = [System.IO.Path]::Combine("C:\rootpath", $Using:filename)
Write-Host $concatenated
}
This will yield the output which we want, that is:
C:\rootpath\somefile.txt
So what you need to do is to either pass it as an argument to the Invoke-Command function, using the -ArgumentList parameter, or (since you are using PowerShell v3) indicate that your variable is a local variable and use the Using scope modifier like the examples above.

Related

Problem when uninstall direct from msi location path & in for loop

I try to uninstall a msi file, but when I try this via array I get an error (cant find installation package)
When I do the same but not in array - it works
for ($i=0; $i -lt $msiArrayClean.length; $i++){
Write-Host $msiArrayClean[$i]
& msiexec.exe /x $msiArrayClean[$i]
}
here the output of Write Host
How i come to $msiArrayClean
$msiCache = get-wmiobject Win32_Product | Where-Object Name -like "*7-Zip*" | Format-Table LocalPackage -AutoSize -HideTableHeaders
$msiString = $msiCache | Out-String
$msiArrayWithEmptyLines = $msiString -split "`n"
$msiArray = $msiArrayWithEmptyLines.Split('', [System.StringSplitOptions]::RemoveEmptyEntries)
$msiArrayCleanString = $msiArray | Out-String
$msiArrayClean = $msiArrayCleanString -split "`n"
A few caveats up front:
Format-* cmdlets output objects whose sole purpose is to provide formatting instructions to PowerShell's output-formatting system - see this answer. In short: only ever use Format-* cmdlets to format data for display, never for subsequent programmatic processing.
The CIM cmdlets (e.g., Get-CimInstance) superseded the WMI cmdlets (e.g., Get-WmiObject) in PowerShell v3 (released in September 2012). Therefore, the WMI cmdlets should be avoided, not least because PowerShell (Core) (version 6 and above), where all future effort will go, doesn't even have them anymore. For more information, see this answer.
Use of the Win32_Product WMI class is discouraged, both for reasons of performance and due to potentially unwanted side effects - see this Microsoft article.
An alternative - available in Windows PowerShell only (not in PowerShell (Core) 7+) - is to use the following to get uninstall command lines and execute them via cmd /c:
Get-Package -ProviderName Programs -IncludeWindowsInstaller |
ForEach-Object { $_.meta.attributes['UninstallString'] }
If you need to stick with Win32_Product:
# Get the MSI package paths of all installed products, where defined.
$msiCache = (Get-CimInstance Win32_Product).LocalPackage -ne $null
foreach ($msiPackagePath in $msiCache) {
if (Test-Path -LiteralPath $msiPackagePath) {
# Note that msiexec.exe runs *asynchronously*.
# Use Start-Process -Wait to wait for each call to complete.
& msiexec.exe /x $msiPackagePath
} else {
Write-Warning "Package not found: $msiPackagePath"
}
}
I don't like reaching to WMI, since its perfomance is the issue. I prefer to do it via registry and it worked for me many times. Code explanation in comments.
$name = "7-zip"
#Get all items from registry
foreach ($obj in Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall") {
#Get DisplayName property of registry
$dname = $obj.GetValue("DisplayName")
#Search for given name
if ($dname -like "*$name*") {
#Get uninstall string (it gets you msiexec /I{APPID})
$uninstString = $obj.GetValue("UninstallString")
foreach ($line in $uninstString) {
#Getting GUID from "{" to "}""
$found = $line -match '(\{.+\}).*'
if ($found) {
#If found - get GUID
$appid = $matches[1]
Write-Output "About to uninstall app $appid"
#Start uninstallation
Start-Process "msiexec.exe" -arg "/X $appid /qb" -Wait
}
}
}
}
Edit: Added solution with msi path after Nehat's comment as this works for me (I tried to minimize the code :))
$msiCache = get-wmiobject Win32_Product | Where-Object Name -like "*7-Zip*" | Format-Table LocalPackage -AutoSize -HideTableHeaders
foreach ($msi in $msiCache | Out-String) {
if ([string]::IsNullOrEmpty($msi)) {
continue
}
Write-Host $msi
Start-Process "msiexec.exe" -arg "/x $msi" -Wait
}

Powershell passing named parameters from file

i have a problem that is troubling my mind.
I want to automatically execute powershell scripts with named arguments but within another powershell script (that will act as a script deamon).
For example:
One of the scripts that get called has this parameters
param(
[int]$version,
[string]$user,
[string]$pass,
[string]$domain,
)
The powershell script deamon now loads the file and arguments like this
$argumentsFromScript = [System.IO.File]::ReadAllText("C:\params.txt") $job = Start-Job { & "ps1file" $arguments}
The params.txt contains the data like this
-versionInfo 2012 -user admin -pass admin -domain Workgrup
But when i try to execute this code obviously the whole $argumentsFromScript variable will be seen as parameter 1 (version) and i end up with an error, that "-versionInfo 2012 -user admin -pass admin -domain Workgrup" cannot be converted to Int32...
Do you guys have any idea how i can accomplish this task?
The Powershell deamon does not know anything about the parameters. He just needs to execute scripts with given named parameters. The params.txt is just an example. Any other file (csv,ps1,xml,etc) would be fine, i just want to automatically get the named parameters passed to the script.
Thank you in advance for any help or advice..
Try this:
#'
param ([string]$logname,[int]$newest)
get-eventlog -LogName $logname -Newest $newest
'# | sc c:\testfiles\testscript.ps1
'-logname:application -newest:10' | sc c:\testfiles\params.txt
$script = 'c:\testfiles\testscript.ps1'
$arguments = 'c:\testfiles\params.txt'
$sb = [scriptblock]::Create("$script $(get-content $argumentlist)")
Start-Job -ScriptBlock $sb
I guess you want this:
$ps1 = (Resolve-Path .\YourScript.ps1).ProviderPath
$parms = (Resolve-Path .\YourNamedParameters.txt).ProviderPath
$job = sajb -ScriptBlock {
param($ps1,$parms)
iex "$ps1 $parms"
} -ArgumentList #(
$ps1,
[string](gc $parms)
)
# if you wanna see the outcome
rcjb $job -Wait

Running Get-ChildItem on UNC path works in Powershell but not in Powershell run in batch file

I am writing a batch file that executes a Powershell script that at one point loops items with UNC paths as attributes and uses Get-ChildItem on those paths. In a minimal version, this is what is happening in my scripts:
Master.bat
powershell -ExecutionPolicy ByPass -File "Slave.ps1"
Slave.ps1
$foo = #{Name = "Foo"}
$foo.Path = "\\remote-server\foothing"
$bar = #{Name = "Bar"}
$bar.Path = "\\remote-server\barthing"
#( $foo, $bar ) | ForEach-Object {
$item = Get-ChildItem $_.Path
# Do things with item
}
The problem I'm running into is that when I run Master.bat, it fails at Get-ChildItem with an error along the lines of
get-childitem : Cannot find path '\\remote-server\foothing' because it does not exist.
However, it seems to work perfectly fine if I run the Slave.ps1 file directly using Powershell. Why might this be happening only when the Master.bat file is run?
Things I have tried
Prepending the UNC paths with FileSystem:: with providers http://powershell.org/wp/2014/02/20/powershell-gotcha-unc-paths-and-providers/
Making sure there are no strange characters in the actual paths
Using the -literalPath parameter instead of the plain -path parameter for Get-ChildItem
Running Get-ChildItem \\remote-server\foothing in PowerShell and succeeding to verify connection to the remote server
I have found this issue when running scripts referring to UNC paths - but the error only occurs when the root of the script is set to a non file system location. e.g. PS SQLSEVER\
So the following fails with the same error:
cd env:
$foo = #{Name = "Foo"}
$foo.Path = "\\remote-server\foothing"
$bar = #{Name = "Bar"}
$bar.Path = "\\remote-server\barthing"
#( $foo, $bar ) | ForEach-Object {
$item = Get-ChildItem $_.Path
# Do things with item
Write-Host $item
}
So my resolution was to ensure that the PS prompt was returned to a file system location before executing this code. e.g.
cd env:
$foo = #{Name = "Foo"}
$foo.Path = "\\remote-server\foothing"
$bar = #{Name = "Bar"}
$bar.Path = "\\remote-server\barthing"
cd c: #THIS IS THE CRITICAL LINE
#( $foo, $bar ) | ForEach-Object {
$item = Get-ChildItem $_.Path
# Do things with item
Write-Host $item
}
I hope this helps - I would be very happy with the bounty as this is my first answer on stack overflow.
P.S. I forgot to add - the PS command prompt root may be set by auto loaded modules in the configuration of your machine. I would check with Get-Location to see if you are actually executng from a non FileSystem location.
Rory's answer provides an effective workaround, but there's a solution that doesn't require changing the current location to a FileSystem provider location first:
Prefix your UNC paths with FileSystem:: to ensure that they are recognized correctly, irrespective of the current location:
$foo = #{
Name = "Foo"
Path = "FileSystem::\\remote-server\foothing"
}
$bar = #{
Name = "Bar"
Path = "FileSystem::\\remote-server\barthing"
}
Alternatively, here is a tweak to Rory's answer to avoid changing the current location session-globally (to preserve whatever the current location is), using Push-Location and Pop-Location:
try {
# Switch to the *filesystem provider's* current location, whatever it is.
Push-Location (Get-Location -PSProvider FileSystem)
# Process the paths.
$foo, $bar | ForEach-Object {
$item = Get-ChildItem $_.Path
# Do things with item
}
} finally {
# Restore the previous location.
Pop-Location
}
Optional background information
This excellent blog post explains the underlying problem (emphasis added):
PowerShell doesn't recognize [UNC paths] as "rooted" because they're not on a PSDrive; as such, whatever provider is associated with PowerShell's current location will attempt to handle them.
Adding prefix FileSystem:: unambiguously identifies the path as being a FileSystem provider path, irrespective of the provider underlying the current location.
I read somewhere else about the Push-Location and Pop-Location commands to counter this kind of problem - I landed on your question while manually, step-by-step, testing a new routine where the script has push/pop, but I forgot to do them on my PS window. After checking #Rory's answer I noticed I was on PS SQLServer:\ instead of PS C:\ prompt.
So a way to use this on your "slave" script would be:
$foo = #{Name = "Foo"}
$foo.Path = "\\remote-server\foothing"
$bar = #{Name = "Bar"}
$bar.Path = "\\remote-server\barthing"
#( $foo, $bar ) | ForEach-Object {
$item = Get-ChildItem $_.Path
Push-Location
# Do things with item
Pop-Location
}
Thought of adding the Push/Pop before and after the # Do things because it seems that it's those things that change the location.

powershell condition not processing all values in an array

I am newb in powershell but keen to put it into good use. I am working on a script which should do the following:
Check for the existence of a specific folder in a specific location (mapped drive)
If the folder exists, then return a listing
If the folder does not exist, then create it.
Ideally, I would like to improve it in terms of check-if exists-remove-item (subdir); check-if not exists-create
This is to facilitate the automation of an archiving process for a specific piece of software. What I have right now sort of works but I cannot figure out how to make it do exactly what I want.
Here is the code:
$X = #("Server1", "Server2", "Server3")
$ChkFile = "f:\archive\$server\AABackup"
$myFolder = "f:\archive\$server"
$DirExists = Test-Path $ChkFile
Foreach ($server in $x){
IF ($DirExists -eq $True) {
Remove-Item $ChkFile -recurse
import-Module "AppAssurePowerShellModule"
start-archive -protectedserver $server -startdate "18/03/2013 5:30 PM" -path "f:\archive\$server"
}
Elseif ($DirExists -ne $True) {
New-Item -path $myFolder -itemType "directory"
import-Module "AppAssurePowerShellModule"
start-archive -protectedserver $server -startdate "18/03/2013 5:30 PM" -path "f:\archive\$server"
}
}
Yes I know it is rough... It's my first attempt though so I could definitely benefit from the more experienced scripters here.
Thanks in advance.
You're setting $ChkFile, $myFolder and $DirExists before the loop, which means that $server doesn't have a value yet, and -- since variables are evaluated immediately -- these variables will contain garbage.
You need to move those three statements inside the foreach loop.
You also don't need to compare -eq $true; this would be simpler:
if ($dirExists) {
# ...
}
else {
# ...
}
Oh, and you only need to import the module once -- do it at the top of the script.
Also, in terms of style: PowerShell keywords should generally be in lowercase: foreach, if, elseif; be consistent when invoking cmdlets (you have a mixture of lower-case and Upper-Case and lower-Case. Note that these don't make any real difference, but using a consistent style makes the code easier to read for someone else coming to it. I'm basing those rules on what I've seen on TechNet, PoshCode, and here, by the way -- they're definitely subjective.
And, personally, I use $lowerCase for local variables, and $UpperCase for parameters (because it makes the auto-generated help text look nicer).
Give this a shot.
$specificPath = Z:\MYDir\MySubDir
if(!(Test-Path $SpecificPath))
{
Mkdir $SpecificPath
}
Else
{
Get-ChildItem $specificPath
}
Explanation:
This checks for the existence of the path contained in $SpecificPath using Test-Path, which will return a Boolean value.
Since I used the (!()) syntax in my IF statement, it will try to evaluate the statement to false, IF the path DOES NOT EXIST, it will run the first block of code.
MkDir is an alias for New-ItemProperty, if you pass just a path to Mkdir it will make a directory, similar to the windows MkDir command.
If the statement contained in the IF statement does not evaluate to false, the ELSE block will run, and execute a get-childitem on the $specificpath variable.

How to change read attribute for a list of files?

I am powershell newbie. I used a sample script and made substitute from get-item to get-content in the first line.
The modified script looks like below:
$file = get-content "c:\temp\test.txt"
if ($file.IsReadOnly -eq $true)
{
$file.IsReadOnly = $false
}
So in essence I am trying to action items contained in test.txt stored as UNC paths
\\testserver\testshare\doc1.doc
\\testserver2\testshare2\doc2.doc
When running script no errors are reported and no action is performed even on first entry.
Short answer:
sp (gc test.txt) IsReadOnly $false
Long answer below
Well, some things are wrong with this.
$file is actually a string[], containing the lines of your file. So the IsReadOnly property applies to the string[] and not to the actual files represented by those strings, which happen to be file names.
So, if I'm understanding you correctly you are trying to read a file, containing other file names, one on each line. And clear the read-only attribute on those files.
Starting with Get-Content isn't wrong here. We definitely are going to need it:
$filenames = Get-Content test.txt
Now we have a list of file names. To access the file's attributes we either need to convert those file names into actual FileInfo objects and operate on those. Or we pass the file names to a -Path argument of Set-ItemProperty.
I will take the first approach first and then get to the other one. So we have a bunch of file names and want FileInfo objects from them. This can be done with a foreach loop (since we need to do this for every file in the list):
$files = (foreach ($name in $filenames) { Get-Item $name })
You can then loop over the file names and set the IsReadOnly property on each of them:
foreach ($file in $files) {
$file.IsReadOnly = $false
}
This was the long and cumbersome variant. But one which probably suits people best with no prior experience to PowerShell. You can reduce the need for having multiple collections of things lying around by using the pipeline. The pipeline transports objects from one cmdlet to another and those objects still have types.
So by writing
Get-Content test.txt | Get-Item | ForEach-Object { $_.IsReadOnly = $false }
we're achieving exactly the same result. We read the contents of the file, getting a bunch of strings. Those are passed to Get-Item which happens to know what to do with pipeline input: It treats those objects as file paths; exactly what we need here. Get-Item then sends FileInfo objects further down the pipeline, at which point we are looping over them and setting the read-only property to false.
Now, that was shorter and, with a little practise, maybe even easier. But it's still far from ideal. As I said before, we can use Set-ItemProperty to set the read-only property on the files. And we can take advantage of the fact that Set-ItemProperty can take an array of strings as input for its -Path parameter.
$files = Get-Content test.txt
Set-ItemProperty -Path $files -Name IsReadOnly -Value $false
We are using a temporary variable here, since Set-ItemProperty won't accept incoming strings as values for -Path directly. But we can inline this temporary variable:
Set-ItemProperty -Path (Get-Content test.txt) -Name IsReadOnly -Value $false
The parentheses around the Get-Content call are needed to tell PowerShell that this is a single argument and should be evaluated first.
We can then take advantage of the fact that each of those parameters is used in the position where Set-ItemProperty expects it to be, so we can leave out the parameter names and stick just to the values:
Set-ItemProperty (Get-Content test.txt) IsReadOnly $false
And then we can shorten the cmdlet names to their default aliases:
sp (gc test.txt) IsReadOnly $false
We could actually write $false as 0 to save even more space, since 0 is converted to $false when used as a boolean value. But I think it suffices with shortening here.
Johannes has the scoop on the theory behind the problem you are running into. I just wanted to point out that if you happen to be using the PowerShell Community Extensions you can perform this by using the Set-Writable and Set-ReadOnly commands that are pipeline aware e.g.:
Get-Content "c:\temp\test.txt" | Set-Writable
or the short, aliased form:
gc "c:\temp\test.txt" | swr
The alias for Set-ReadOnly is sro. I use these commands weekly if not daily.

Resources