Select-Object output Directory path to Variable - powershell

I have a program that I need to uninstall on a number of systems and unfortunately the way this software is silently uninstalled is by running an exe called "uninstall.exe" this could be in 1 of many directories per system so I have a PowerShell script I'm trying to make work where I look for "Uninstall.exe" in any of the directories it may be located with:
get-childitem -recurse -include "uninstall.exe" 'C:\Program Files (x86)\mainoffice-*'
This seems to work as the output gives me the full path of the folder where "Uninstall.exe" is located. I will need to then run:
uninstall.exe --mode unattended
So I think I will need to output the full path where "Uninstall.exe" is located to a variable. I tired to do this with:
$uninstalldir = get-childitem -recurse -include "uninstall.exe" 'C:\Program Files (x86)\wwworksmainoffice-*' | Select-Object FullName
I can't seem to just get this to output the directory path so I can do something like:
$uninstalldir --mode unattended
Can anyone try help me understand where I might be going wrong here?

This pipeline:
... |Select-Object FullName
... will create a new object with a single property FullName, so in order to resolve just the string value stored in FullName you'd need to dereference it like this:
$uninstalldir.FullName
This is still just a value expression - it you want PowerShell to invoke a string as if it were a command or program, you'll need the & call operator:
& $uninstalldir.FullName --mode unattended
Alternatively, you could've used Select-Object -ExpandProperty FullName or ForEach-Object FullName to get just the raw value of the FullName property instead of sticthing it onto a new object:
$uninstalldir = Get-ChildItem ... |Select-Object -ExpandProperty FullName
# or
$uninstalldir = Get-ChildItem ... |ForEach-Object -MemberName FullName
At which point the variable itself resolves to the string value:
& $uninstalldir --mode unattended

Related

is there a way to check if a program is installed just by filename in powershell?

I am trying to create a powershell script to auto install all .msi and .exe files silently in a directory. However while doing this i want to check if any of the programs are already installed.
I know i can get all installed files in the system like below
$32bit_softwares = Get-ItemProperty HKLM:\SOFTWARE\wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
Select-Object DisplayName,DisplayVersion,Publisher,InstallDate
$64bit_softwares = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\* |
Select-Object DisplayName,DisplayVersion,Publisher,InstallDate
$all_softwares = $32bit_softwares+$64bit_softwares
and i can get the filenames of the files in the directory like below:
$directoryRead = Read-Host -Prompt "enter directory"
$fileNames = Get-ChildItem $directory -Recurse -include *.exe,*.msi | ForEach-Object {$_.name}
How can i compare these 2 in a loop? like
$all_softwares.DisplayName -like "$softwareName*"
I am not sure a like filter above will do the job as filenames will be like examplename.exe
Thanks in advance.
So the problem that I see and I think you are asking about is that the installer filename will be different than the software name you pull out of the Registry. With the difference it will be hard to match up exactly.
Is the set of MSI's and/or EXE's (the installers) a known, static set? Would it be possible to setup a hash table (dictionary) mapping between the Registry name and the installer name?
This would make matching exactly while looping through the installers and doing a .Contains on the array from the Registry easier.

Azure Devops - PowerShell task on self-hosted agent

I have an Azure Devops deployment pipeline setup which is building and I am able to deploy to a self hosted virtual machine with no issue.
I have the following powershell script that correctly clears down my destination directory leaving 2 folders that are not part of source control
Get-ChildItem -Path 'C:\inetpub\wwwroot\testDeploy\' -Recurse -exclude "pod","photos" |
Select -ExpandProperty FullName |
Where {$_ -notlike '*\pod\*' -and $_ -notlike '*\photos\*'} |
sort length -Descending |
Remove-Item -force
I have tried adding a "PowerShell Script" task but i'm don;t know how to get the PowerShell script in to a folder that the task can access i.e. $(System.DefaultWorkingDirectory). Can anyone advise how I should be either generating the file or where to store it in my repo that is then accessible by the self-hosted Windows agent
Agree with Shayki, you can create a powershell(.ps1) file in repos and paste your script in it to achieve that. And then, use powershell task to execute the script which in ps1 file.
But, as you said that you want it be maintained within the repos easily. Need made some change on your script:
Param(
[string]$RootPath,
[string]$File1,
[string]$File2,
[string]$NonLike1,
[string]$NonLike2
)
Get-ChildItem -Path $RootPath -Recurse -include $File1,$File2 |
Select -ExpandProperty FullName |
Where {$_ -notlike $NonLike1 -and $_ -notlike $NonLike2} |
sort length -Descending |
Remove-Item -Recurse -force
The first change is, you need to replace the hard code with variable. Pass the value with task, this is a good way to maintain your script.
The second which also the important change is add -Recurse after Remove-Item, or you will get the error showed below while the value of $RootPath is hard code, such as 'C:\Users\'.
Remove-Item : Windows PowerShell is in NonInteractive mode. Read and
Prompt functionality is not available.
And then, you can add task in your build pipeline. Add the Script path where the .ps1 file located and input the Arguments with the value:
If you want to access $(System.DefaultWorkingDirectory), pass it to $RootPath.
Hope my sample can help you achieve what you want.

How can I pass the results of get-childitem into a command?

I have several directories, subdirectories, etc in a directory structure and some of them will have various matching files. e.g. if X.config.default exists in a directory it will also have a corresponding X.config.build
c:\Stuff\dir1
web.config.default
web.config.build
c:\Stuff\dir2
app.config.default
app.config.build
c:\Stuff\dir2\sub2
foo.config.default
foo.config.build
bar.config.default
bar.config.build
This will display all file names matching *.config.default and their corresponding directory
get-childitem -Recurse *.* -Filter *.config.default | Select Name, Directory
But instead of displaying the files and their path, I want to do something for each "match". In this case I want to call a program called ctt and send it three arguments. ctt is called as follows:
ctt s:<source file> t:<transform file> d:<destination file>
Assume the first match is called fubar in directory c:\Stuff\dir1, the ctt command executed should look like:
ctt s:c:\Stuff\dir1\fubar.config.default t:c:\Stuff\dir1\fubar.config.build d:c:\Stuff\dir1\fubar.config pw
I'm guessing there are a few ways of doing this. Piping get-childitem results into a command, or sending them to some sort of collection on which I can do a foreach loop.
Thanks
There are a few different ways of approaching this. If you're on an older version of PowerShell, you'd most likely just use the ForEach-Object cmdlet.
Get-ChildItem -Path c:\Stuff\* -Recurse -Filter *.config.default |
ForEach-Object -Process {
$BuildName = $PSItem.Name.Split('.')[0] ### Get just the "fubar" part.
ctt s:"$($PSItem.FullName)" t:"$($PSItem.Directory.FullName)\$BuildName.config.build" d:"$($PSItem.Directory.FullName).config" pw
}
On newer versions of PowerShell, starting with 4.0, you can use the ForEach() method syntax.
http://social.technet.microsoft.com/wiki/contents/articles/26489.powershell-4-0-where-and-foreach-method-syntax.aspx
(Get-ChildItem -Path c:\Stuff\* -Recurse -Filter *.config.default).ForEach({
$BuildName = $PSItem.Name.Split('.')[0] ### Get just the "fubar" part.
ctt s:"$($PSItem.FullName)" t:"$($PSItem.Directory.FullName)\$BuildName.config.build" d:"$($PSItem.Directory.FullName).config" pw
}

Automating MS Updates with Powershell

I am trying to Automate my Microsoft update process based on the KB/MSU packages I download. I have a WMIC csv file that I am able to install based on simple If else statement. I would like to use the list extract just the "KBXXXXX" from the downloaded files and compare it to the list of installed KBs and discover what is missing.
KB install List (WMIC output):
KB123456
KB234567
KB345678
Downloaded KB file list format $KBUpdateList:
Name
WINDOWS6.1-KB2533552-X64.MSU
WINDOWS6.1-KB2533552-X86.MSU
WINDOWS6.1-KB2539635-X64.MSU
Windows6.1-KB958488-v6001-x64.MSU
Here is what I have that is not working to pull KB number:
PowerShell script to list the MSU files under the selected folder:
$Dir = get-childitem $folder -recurse
$KBUpdateList = $Dir | where {$_.extension -eq ".msu"}
$KBUpdatenames = $KBUpdateList | format-table name
$KBNumberonly = $KBUpdateList.split("-")[1]
The split fails and I can't find a fix. I just want to return the KBXXXX number so I can run my foreach statement that follows. Thank You
This should suffice for what you are looking for.
$KBUpdatenames = get-childitem $folder -recurse -Filter "*.msu" | Select-Object -Expand Name
$KBNumberonly = $KBUpdatenames | ForEach-Object{$_.split("-")[1]}
Use Get-ChildItem to get the files of type ".msu". Using -Filter is more efficient than Where-Object in most case where you are just looking for extensions. Then we expand just the names of the files with Select-Object
As for the Format-Table in your code I will refer you to an answer about Format-Taco that I enjoy.

Using PowerShell to get a file name in a directory

I have a directory that contains a file called Bruce.txt. I need to copy the file name to a flat file. I tried using the copy-item command but that copies the contents not the name.
Is there a command to copy Bruce.txt (the name not the contents) to a flat file? So after it completes there will be a file called process.txt and its contents will be Bruce.txt. I tried using
Copy-Item -Path "C:\Users\Bruce\deploy\*.txt" -Destination "C:\Users\Bruce\process.txt".
Inside of deploy is a text file called Bruce.txt with the contents of select count() from EMP_NR.
I need Bruce.txt the name copied to process.txt not the Select count() etc.
For Shell script I use the ls command and it works wonderful what can I use for Powershell?
You need to use the Get-ChildItem cmdlet.
Get-ChildItem C:\Users\Bruce\deploy\*.txt | Select-Object -ExpandProperty Name | Out-File C:\Users\Bruce\process.txt -Force -Append
However, as you're using PowerShell, ls would actually work for you here, as would gci and dir as they're all aliases for Get-ChildItem:
> get-alias | ? {$_.DisplayName -ilike "*get-childitem*" }
CommandType Name
----------- ----
Alias dir -> Get-ChildItem
Alias gci -> Get-ChildItem
Alias ls -> Get-ChildItem
You can also use > or >> instead of piping to Out-File if you so wish.
Because the Get-Childitem cmdlet returns a list of objects, you then need to also select which information you want to extract from the object. If you do a ls in a directory with some content, you will see the contents are formatted into a table.
By using the Select-Object cmdlet, you can extract the object properties you want to write to your file, in this case the Name property.