MSIExec via Powershell Install - powershell

Find the list of MSI Files from a directory and install on given PC remotely or locally . I want to be able to to run a script that will install 8 separate MSI files in a given directory 1 by 1. I found this script and think it work but i feel as if it is missing something right?
foreach($_msiFiles in
($_msiFiles = Get-ChildItem $_Source -Recurse | Where{$_.Extension -eq ".msi"} |
Where-Object {!($_.psiscontainter)} | Select-Object -ExpandProperty FullName))
{
msiexec /i $_msiFiles /passive
}

It would help you to understand what is going on here. I would write it something like this:
Declare source Directory:
$source = “\\path\to\source\folder”
Put each child .msi object into an array:
$msiFiles = Get-Childitem $source -File -recurse | Where-Object {$_.Extension -eq “.msi”}
Iterate the array to run each .msi:
Foreach ($msi in $msiFiles) {
Msiexec /I “$($msi.FullName)” /passive
}
This is of course just an explanation of what you are doing. It does not include any error handling, checking for return codes, or remote command syntax, etc. etc.

Related

Use Invoke-Item calling .exe with variable folder path and passing /qn argument

I struggling to find whats wrong with my code.
Im trying to uninstall an app on several Windows 10 clients, where the path to the .exe is different on any machine. I use Get-ChildItem to get that specific folder. Now im trying to execute that path with the .exe at the end and add a /qn trigger to silently deinstall.
However, i always get back errors regarding the Invoke-Item function.
Here is my script:
First im trying to find the variable foldername inside the app folder:
$path_to_exe = Get-ChildItem -Path "C:\Program Files\ApplicationName" -Include "installer.exe" -Recurse |
Where-Object { $_.FullName -match '\{(.*)\}' }
Which gives me the path as a Get-ChildItem response.
Now i convert the path given from Get-ChildItem to a "Normal" Path
$path_to_exe = Convert-Path $Path_to_exe.PSPath
Now i try to call the path i found with the /qn trigger to silently deinstall.
"`"$path_to_exe`"" + " /qn" | Invoke-Item
Im 100% sure that my approach is a beginner tier one.
If anyone has a better idea, please educate me.
Thanks :)
EDIT:
The manufacturer states i should use the following:
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{1D9F5D88-12AA-427F-8A33-DED71D60E4D9} - MsiExec.exe /X{1D9F5D88-12AA-427F-8A33-DED71D60E4D9}
Does anybody have an idea how i can extract that guid from the Get-ChildItem registry query?
get-package "*applicationname*" | uninstall-package
I finally figured it out! As i knew, i had the wrong approach. I was making it way to complicated trying to parse the EXE file to a seperate powershell instance and adding arguments.
I rewrote my whole code using the msiexec function.
So first i needed to find out the GUID of the app using the Get-ChildItem function.
$Path_to_GUID = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall |
Get-ItemProperty |
Where-Object {$_.DisplayName -match "Application Name" } |
Select-Object -Property DisplayName, UninstallString
Now i had the desired path of that Get-ChildItem object, with the .UninstallString property.
Since i only want the GUID itself, i used the Split-Path function to separate the path by the folder name/GUID which in the beginning somewhat looked like this:
HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{xxxxxx-xxxx-xxxx-xxxx}\installer.exe
So using the Split-Path function i first only cut the -Parent part.
$Path_to_GUID = Split-Path -Path $Path_to_GUID.UninstallString -Parent
Which gave me the path like this, without the installer.exe at the end:
HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{xxxxxx-xxxx-xxxx-xxxx}
So now i only want the part of the path with the curly braces using the -Leaf argument:
$Path_to_GUID = Split-Path $Path_to_GUID -Leaf
Which gave me the desired string:
{xxxxxx-xxxx-xxxx-xxxx}
Now i just had to uninstall using msiexec and adding the silent parameter:
msiexec /x $Path_to_GUID /qn
Here's the full code (Powershell):
$Path_to_GUID = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall |
Get-ItemProperty |
Where-Object {$_.DisplayName -match "Application Name" } |
Select-Object -Property DisplayName, UninstallString
$Path_to_GUID = Split-Path -Path $Path_to_GUID.UninstallString -Parent
$Path_to_GUID = Split-Path $Path_to_GUID -Leaf
msiexec /x $Path_to_GUID /qn

Using Where-Object for accessing folders with administrator permission

I would like to scan windows folder to find files that are created in a specific data range and export it to a csv file. Although I have opened a powershell as administrator, I still see some "access denied" messages.
PS C:\Windows> Get-ChildItem . -recurse | Where-Object { $_.CreationTime -ge "07/01/2021" -and $_.CreationTime -le "07/31/2021" } | Export-Csv 'e:\scans.csv'
Get-ChildItem : Access to the path 'C:\Windows\CSC' is denied.
...
How can I fix that?
A known trick is to install the sysInternals suite to use psexec.exe and to run your script in a powershell as System:
psexec.exe -i -s powershell.exe
Then at least C:\windows\CSC is available.

Run all .exe in a folder

I'm playing with malware in a VM and every script I try gets stuck. Basically I need to run every .exe in a folder. Tried batch files using start, powershell, etc. The issue happens when AV moves some file to quarentine, or some process keep running then the script doesn't jump to the next one.
CMD start works but shows popups when doesn't find some file, then you have to keep clicking to jump to the next file.
These works but get stuck after a while:
Get-ChildItem 'C:\Users\LAB\Desktop\test' | ForEach-Object {
>> & $_.FullName
>> }
Same here:
for %%v in ("C:\Users\LAB\Desktop\test\*.exe") do start "" "%%~v"
and here:
for %%i in (C:\Users\LAB\Desktop\test\*.exe) do %%i
You need to provide some form of code to allow us to help you troubleshoot it; this is not a request a script page.
Anyways, you would be looking at something like this:
#Assuming the .exe's are located in C Root.
Get-ChildItem -Path C:\ | Where-Object {$_.Extension -like ".exe"}| Foreach {Start-Process $_.FullName}
#In Ps, we like to filter as far left as possible for faster results.
Get-ChildItem -Path C:\ -File "*.exe" | Foreach {Start-Process $_.FullName}
#Running the commands as jobs so it doesnt wait on any to finish before running the next.
Start-Job { Get-ChildItem -Path C:\ -File "*.exe" | Foreach {Start-Process $_.FullName} }
Start-Sleep 2
Get-Job | Remove-Job
Please refer to the following link: How to ask a question

How can I find the length of the first line of a file in batch or powershell

I have to read each file in a folder and determine the length of the first line of each, then do something depending on whether or not that length is what is should be in a table. I can loop through each file in batch and have %%f as the file, but how do I get that length and assign it to a variable?
If there is a way to do this in Powershell using a batch file, that would help, but I would need to know how to call the Powershell from the batch file also.
The simple PowerShell code would look something like this:
param($path)
Get-ChildItem $path -File |
Select FullName,#{Label="1stLineLength";Expression={(Get-Content $_.FullName -First 1).Length}}
So the first argument will be taken as the path of the script. Then to call it from batch I borrow the answer to this SO question.
Powershell.exe -executionpolicy remotesigned -File m:\Scripts\firstlinelength.ps1 "C:\temp"
That will get output like this on console.
FullName 1stLineLength
-------- -------------
C:\Users\mcameron\CleansedBigFile.txt 4
This code assumes that you have at least PowerShell v3.0 for the -First and -File parameter and switch. I would like to think that most batch code can be converted easily to a PowerShell equivalent so if your environment allows you consider converting to the powerful PowerShell.
Some of your question is pretty vague (what table?). But in general, you don't need a batch file at all. PowerShell example:
Get-ChildItem -File | ForEach-Object {
$firstLineLength = (Get-Content $_ | Select-Object -First 1).Length
if ( $firstLineLength -gt $whateverFromTable ) {
...
}
}
Note that the -File parameter of Get-ChildItem doesn't exist before PowerShell v3. For PowerShell v2 and below, you would replace Get-ChildItem -File with Get-ChildItem | Where-Object { -not $_.PSIsContainer }.

Using Powershell to Register a file in the Gac

Is there a simple way to in PowerShell (I imagine using gacutil.exe) to read from a text document a path\assembly and register it in the GAC? So for example a .txt file that looks like:
c:\test\myfile.dll
c:\myfile2.dll
d:\gac\gacthisfile.dll
The PowerShell script would read that into a stream and then run gacutil on each of those assemblies found? I guess it would be something like:
#read files into array?
foreach ($file in Get-ChildItem -Filter "*.dll" )
{
Write-Host $file.Name
C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\gacutil.exe /nologo /i $file.Name
}
How about let the .Net worry about gacutil?
# load System.EnterpriseServices assembly
[Reflection.Assembly]::LoadWithPartialName("System.EnterpriseServices") > $null
# create an instance of publish class
[System.EnterpriseServices.Internal.Publish] $publish = new-object System.EnterpriseServices.Internal.Publish
# load and add to gac :)
get-content fileOfDlls.txt | ?{$_ -like "*.dll"} | Foreach-Object {$publish.GacInstall($_)}
If you sort out your text file such that the each dll is on a separate line, you could use the Get-Content command and pipe each to a filter that did your command:
filter gac-item { C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\gacutil.exe /nologo /i $_}
get-content fileOfDlls.txt | ?{$_ -like "*.dll"} | gac-item
I would suggest calling the function to add an assembly to the GAC something following PowerShell guidelines like Add-GacItem. Also the location of gacutil.exe varies based on your system. If you have VS 2008 installed, it should be at the location shown below.
function Add-GacItem([string]$path) {
Begin {
$gacutil="$env:ProgramFiles\Microsoft SDKs\Windows\v6.0A\bin\gacutil.exe"
function AddGacItemImpl([string]$path) {
"& $gacutil /nologo /i $path"
}
}
Process {
if ($_) { AddGacItemImpl $_ }
}
End {
if ($path) { AddGacItemImpl $path }
}
}
Get-Content .\dlls.txt | Split-String | Add-GacItem
Note that the Split-String cmdlet comes from Pscx. The function isn't super robust (no wildcard support doesn't check for weird types like DateTime) but at least it can handle regular invocation and pipeline invocation.
Do you want to replace gacutil.exe? If not, why not use gacutil's included /il switch?
From the gacutil /h:
/il <assembly_path_list_file> [ /r <...> ] [ /f ]
Installs one or more assemblies to the global assembly cache.
<assembly_list_file> is the path to a text file that contains a list of
assembly manifest file paths. Individual paths in the text file must be
separated by CR/LF.
Example: /il MyAssemblyList.txt /r FILEPATH c:\projects\myapp.exe "My App"
myAssemblyList.txt content:
myAsm1.dll
myAsm2.dll
If you create an alias in your profile (just type $profile at a ps prompt to determine this file location) like so new-alias "gac" ($env:ProgramFiles+"\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe") then you can use gac like so:
get-childitem $basedirectory "*$filter.dll" | foreach-object -process{ WRITE-HOST -FOREGROUND GREEN "Processing $_"; gac /i $_.FullName /f}
the last part is the most important. it calls gacutil with the switches you want.
Hope this helps.
This PowerShell script will add assemblies to the GAC without using GacUtil. http://blog.goverco.com/2012/04/use-powershell-to-put-your-assemblies.html
After downloading the Add-AssemblyToGlobalAssemblyCache.ps1 you can deploy to the gac.
Usage example for adding multiple assemblies Dir C:\MyWorkflowAssemblies | % {$_.Fullname} | .\Add-AssemblyToGlobalAssemblyCache.ps1
See the full documentation by running Get-Help .\Add-AssemblyToGlobalAssemblyCache.ps1 -Detailed
Not wanting to install the Windows 8 SDK on all machines I needed to put assemblies in the GAC to get gacutil, I've written a powershell module using the GAC API. It works with any .Net version. With PowerShell GAC you can do it like so:
Get-Content ListOfAssemblies.txt | Add-GacAssembly