How to find out what version of webdeploy/msdeploy is currently installed? - powershell

I'm looking for something like a Powershell script to check if msdeploy is installed and if it is, what version
I've considered checking "c:\Program Files\IIS" and checking for MSDeploy installations there, but is this always guaranteed to be the install location?
I need this to work on any given server machine

When msdeploy is installed (no matter where in the file system), it will add its install path to the registry at;
HKLM\Software\Microsoft\IIS Extensions\MSDeploy\<version>\InstallPath
and its version information to;
HKLM\Software\Microsoft\IIS Extensions\MSDeploy\<version>\Version
...where <version> is currently 1, 2 or 3 depending on the WebDeploy version you have installed.

Depends on what you consider "version". By the folder name "c:\Program Files\IIS\Microsoft Web Deploy V3", the version is 3, but if you run msdeploy.exe, the version is 7.X

This is what I did in my PowerShell script:
$WebDeployInstalled = Get-WmiObject Win32_Product | ? {$_.Name -like '*Microsoft Web Deploy*'}
if ($WebDeployInstalled -eq $null)
{
$msg = "Microsoft Web Deploy is not found on this machine."
Write-host -BackgroundColor Red -ForegroundColor White $msg
return
}
else
{
$MSDeployPath = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\*" | Select-Object InstallPath
$MSDeployPath = $MSDeployPath.InstallPath
}
HTH

You can use the following PowerShell snippet:
$installPath = $env:msdeployinstallpath
if(!$installPath){
$keysToCheck = #('hklm:\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\3','hklm:\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\2','hklm:\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\1')
foreach($keyToCheck in $keysToCheck) {
if(Test-Path $keyToCheck){
$installPath = (Get-itemproperty $keyToCheck -Name InstallPath -ErrorAction SilentlyContinue | select -ExpandProperty InstallPath -ErrorAction SilentlyContinue)
}
if($installPath) {
break;
}
}
}
If you wrap it into script block then you can call it in remote session.

Related

Powershell to Loop through each user profile to get Version number of software and then create uninstall string and run the command

Hoping someone can give me an idea on how to proceed with the remaining script.
The script is to get Version number of Installed Chrome from that build Build a string for the uninstall as shown below.
I'm stuck on the second part, fine on getting the version number.
What would the logic be next to then iterate through each user profile to run the setup.exe from C:\Users[username]\appdata\Local\Google\Chrome\Application\90.0.4430.72\Installer. The error I am getting is unrecognized cmdlet on the { & $unin}
Thank you
#UserHives - Find all the user profiles in the registry
$UserHives = Get-ChildItem Registry::HKEY_USERS\ |Where-Object {$_.Name -match '^HKEY_USERS\\S-1-5-21-[\d\-]+$'}
$UserProfile = $Env:USERPROFILE
#
foreach($user in $UserHives)
{
#1.Get Version Of chrome
#1. PATH TO SEARCH FOR
$Path = Join-Path $user.PSPath "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome"
If (Test-Path $Path){
$GetVersion = Get-ItemProperty -Path $Path | Select-Object -Property Version
$VersionInstalled = $GetVersion.Version
#create uninstallstring
$UninString = "\Google\Chrome\Application\$VersionInstalled\Installer\setup.exe --uninstall --Channel --chrome --force-uninstall"
$unin = $UserProfile + "" + $UninString
If($VersionInstalled){ & $unin}
}
}
Quote from the docs:
The call operator does not parse strings. This means that you cannot
use command parameters within a string when you use the call operator.
Pass the arguments separately:
$uninArgs = "--uninstall", "--Channel", "--chrome", "--force-uninstall"
$uninExe = "$UserProfile\Google\Chrome\Application\$VersionInstalled\Installer\setup.exe"
if ($VersionInstalled) {
& $uninExe $uninArgs
}

Is there a way to find every single modules which will be needed in script?

I'd like to use a kinda analyzer which will install/import all the needed modules by the script before I run it on distant machine (which could not have it) ......
any idea ?
EDIT
Here's the case :
I'm on my dev machine, I'ved already installed lots of modules of all kind (dhcp, ntfs, remoting, register, etc.)
When I finally got my script (which is a function) to work, I can't be sure of what modules are used....
What I want is to write down, in the 'begin' section, the correct imports before I send my script on remote PCs; to be sure it's gonna run perfectly, you follow ?...
Is there a kinda a third party appplication which can scan my script and give me all needed modules ?
You could do something like this to get help in finding commands used and their source/module names. It's very unpolished, just trying to give the idea.
$scriptblock = {
Write-Host "Nothing here"
$files = Get-ChildItem c:\temp
Get-ADUser someuser
Test-NetConnection www.google.com
}
# Uncomment following lines and enter the path to your script file
# $scriptFile = "Path\to\some\scriptfile"
# $scriptblock = [scriptblock]::Create((Get-Content -raw -Path $scriptFile))
$ast = $scriptblock.Ast
$commands = $ast.FindAll( { $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true)
$commandText = foreach ($command in $commands) {
$command.CommandElements[0].Extent.Text
}
$commandText |
Select-Object -Unique |
Sort-Object |
Select-Object #{
Label = "CommandName"
Expression = { $_ }
},
#{
Label = "Source"
Expression = {
(Get-Command $_).Source
}
}
Output
CommandName Source
----------- ------
Get-ADUser ActiveDirectory
Get-ChildItem Microsoft.PowerShell.Management
Test-NetConnection NetTCPIP
Write-Host Microsoft.PowerShell.Utility
Yeah, you could for example test if the module exists on that particular machine by trying to import it as follows
Try {
Import-Module dbaclone -ErrorAction stop
#ErrorAction required as failing to import is not a terminating action
} Catch {
Write-Verbose -Verbose "Failed to find dbaclone module - installing"
Install-Module dbaclone -AllowClobber -Force
Write-Verbose -Verbose "Installed!"
Import-Module dbaclone
}

Locate MSTest.exe using powershell

I'm in the process of automating my .Net solution build to be completely in PowerShell. I want to locate MSTest.exe using PowerShell.
I used the following script to locate MSBuild.exe and I hope that I can have something similar to locate MSTest.exe
$msBuildQueryResult = reg.exe query "HKLM\SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0" /v MSBuildToolsPath
$msBuildQueryResult = $msBuildQueryResult[2]
$msBuildQueryResult = $msBuildQueryResult.Split(" ")
$msBuildLocation = $msBuildQueryResult[12] + "MSBuild.exe"
Any directions ?
The following works with Visual Studio 2010 and higher[1]:
# Get the tools folder location:
# Option A: Target the *highest version installed*:
$vsToolsDir = (
Get-Item env:VS*COMNTOOLS | Sort-Object {[int]($_.Name -replace '[^\d]')}
)[-1].Value
# Option B: Target a *specific version*; e.g., Visual Studio 2010,
# internally known as version 10.0.
# (See https://en.wikipedia.org/wiki/Microsoft_Visual_Studio#History)
$vsToolsDir = $env:VS100COMNTOOLS
# Now locate msbuild.exe in the "IDE" sibling folder.
$msTestExe = Convert-Path -EA Stop (Join-Path $vsToolsDir '..\IDE\MSTest.exe')
The approach is based on this answer and is generalized and adapted to PowerShell.
It is based on system environment variables VS*COMNTOOLS, created by Visual Studio setup, where * represents the VS version number (e.g., 100 for VS 2010).
Re option A: Sort-Object is used to ensure that the most recent Visual Studio installation is targeted, should multiple ones be installed side by side:
The script block used for sorting first extracts only the embedded version number from the variable name ($_.Name -replace '[^\d]'; e.g., 100 from VS100COMNTOOLS) and converts the result to an integer ([int]); [-1] then extracts the last element from the sorted array - i.e., the variable object whose names has the highest embedded version number - and accesses its value (.Value).
The IDE subfolder, in which MSTest.exe is located is a sibling folder of the tools folder that VS*COMNTOOLS points to.
If MSTest.exe is NOT in the expected location, Convert-Path will throw a non-terminating error by default; adding -EA Stop (short for: -ErrorAction Stop) ensures that the script is aborted instead.
[1]
- I've tried up to Visual Studio 2015; do let me know whether or not it works on higher versions.
- Potentially also works with VS 2008.
Perhaps you are wanting something like this?
$regPath = "HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0"
$regValueName = "MSBuildToolsPath"
$msBuildFilename = "MSBUild.exe"
if ( Test-Path $regPath ) {
$toolsPath = (Get-ItemProperty $regPath).$regValueName
if ( $toolsPath ) {
$msBuild = Join-Path $toolsPath $msBuildFilename
if ( -not (Test-Path $msBuild -PathType Leaf) ) {
Write-Error "File not found - '$msBuild'"
}
}
}
# Full path and filename of MSBuild.exe in $msBuild variable
My way of getting mstest path.
GetMSTestPath function is main function which you call and then if first GetMsTestPathFromVswhere function will find something it returns path if not your will be making a long search for mstest.exe. Usually, it takes approximately 10 sec. I know that this is not the best but at least it is something when you struggle to find mstest.exe. Hope it will be helpful for somebody. :)))
function GetMSTestPath
{
function GetTime()
{
$time_now = Get-Date -format "HH:mm:ss"
return $time_now;
}
function GetMsTestPathFromVswhere {
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
$path = & $vswhere -latest -prerelease -products * -requires Microsoft.Component.MSBuild -property installationPath
#write-host $path
if ($path) {
$tool = join-path $path 'Common7\IDE\MSTest.exe'
if (test-path $tool) {
return $tool
}
return ""
}
}
function SeachForMsTestPath
{
write-host $(GetTime)
$path = Get-ChildItem C:\ -Filter MSTest.exe -Recurse -ErrorAction Ignore | ? { $_.VersionInfo.FileDescription -eq 'Test Execution Command Line Tool' } | Select -First 1
write-host $(GetTime)
return $path
}
$msTestExePath = GetMsTestPathFromVswhere
if ([string]::IsNullOrEmpty($msTestExePath))
{
$msTestExePath = SeachForMsTestPath;
if ([string]::IsNullOrEmpty($msTestExePath))
{
Write-host "MsTest path is not found. Exiting with error"
Exit -1
}
}
return $msTestExePath;
}
Thanks #Bill_Stewart , I used your comments to write this working function:
function Get-MSTest-Location {
$msTests = #()
$searchResults = Get-ChildItem C:\* -Filter MSTest.exe -Recurse -ErrorAction Ignore
foreach($searchResult in $searchResults) {
try{
if(($searchResult.VersionInfo -ne $null) -and ($searchResult.VersionInfo.FileDescription -eq "Test Execution Command Line Tool"))
{ $msTests = $msTests + $searchResult.FullName }
}
catch{}
}
if($msTests.Length -eq 0)
{return "MSTest not found."}
return $msTests[0]
}

I can't figure out why Install-WindowsFeature is timing out

Title. I can't seem to get this to work and I'm miffed as to why. Windows 2012R2 Data Center. The below source is from the install.wim file directly from an install CD. It errors out with the following error:
The server could not update the provided feature files in the time allowed
Below is the code, note it shows which features I'm trying to install. In theory this shouldn't be reaching out to windows update (I think). OTOH, I'm a developer and not a windows admin so I honestly don't know what the underlying behavior is supposed to be. It's the reason why I'm here, at this point I've tried all I can think of.
Under what circumstances does this error occur and how do I go about solving it?
$features = Get-Features
$source = "wim:C:\WindowsCore\install.wim:4"
Install-WindowsFeature -Name $features -IncludeManagementTools -Source $source -WarningAction SilentlyContinue | Out-Null
function Get-Features {
return [string[]] #(
'FileAndStorage-Services'
'Storage-Services'
'Web-Server'
'Web-WebServer'
'Web-Common-Http'
'Web-Default-Doc'
'Web-Dir-Browsing'
'Web-Http-Errors'
'Web-Static-Content'
'Web-Http-Redirect'
'Web-Health'
'Web-Http-Logging'
'Web-Log-Libraries'
'Web-ODBC-Logging'
'Web-Request-Monitor'
'Web-Http-Tracing'
'Web-Performance'
'Web-Stat-Compression'
'Web-Dyn-Compression'
'Web-Security'
'Web-Filtering'
'Web-Basic-Auth'
'Web-Client-Auth'
'Web-Digest-Auth'
'Web-Cert-Auth'
'Web-IP-Security'
'Web-Url-Auth'
'Web-Windows-Auth'
'Web-App-Dev'
'Web-Net-Ext'
'Web-Net-Ext45'
'Web-ASP'
'Web-Asp-Net'
'Web-Asp-Net45'
'Web-CGI'
'Web-ISAPI-Ext'
'Web-ISAPI-Filter'
'Web-Includes'
'Web-WebSockets'
'Web-Ftp-Server'
'Web-Ftp-Service'
'Web-Mgmt-Tools'
'Web-Mgmt-Console'
'Web-Mgmt-Compat'
'Web-Metabase'
'Web-Lgcy-Mgmt-Console'
'Web-Lgcy-Scripting'
'Web-WMI'
'Web-Scripting-Tools'
'Web-Mgmt-Service'
'NET-Framework-Features'
'NET-Framework-Core'
'NET-Framework-45-Features'
'NET-Framework-45-Core'
'NET-Framework-45-ASPNET'
'NET-WCF-Services45'
'NET-WCF-HTTP-Activation45'
'NET-WCF-TCP-PortSharing45'
'RSAT'
'RSAT-Feature-Tools'
'RSAT-SMTP'
'RSAT-SNMP'
'FS-SMB1'
'SMTP-Server'
'SNMP-Service'
'User-Interfaces-Infra'
'Server-Gui-Mgmt-Infra'
'Server-Gui-Shell'
'PowerShellRoot'
'PowerShell'
'PowerShell-V2'
'PowerShell-ISE'
'WAS'
'WAS-Process-Model'
'WAS-Config-APIs'
'WoW64-Support'
)
}
I also tryed to use the Install-WindowsFeature cmdlet to install several features in a setup. I ended using dism because it seems to be less error-prone. You could at least give it a try and maybe get a more detailed error message. Here is the script:
$featuresToInstall = ('FileAndStorage-Services','Storage-Services','Web-Server','Web-WebServer','Web-Common-Http','Web-Default-Doc','Web-Dir-Browsing','Web-Http-Errors',
'Web-Static-Content','Web-Http-Redirect','Web-Health','Web-Http-Logging','Web-Log-Libraries','Web-ODBC-Logging','Web-Request-Monitor',
'Web-Http-Tracing','Web-Performance','Web-Stat-Compression','Web-Dyn-Compression','Web-Security','Web-Filtering','Web-Basic-Auth',
'Web-Client-Auth','Web-Digest-Auth','Web-Cert-Auth','Web-IP-Security','Web-Url-Auth','Web-Windows-Auth','Web-App-Dev','Web-Net-Ext',
'Web-Net-Ext45','Web-ASP','Web-Asp-Net','Web-Asp-Net45','Web-CGI','Web-ISAPI-Ext','Web-ISAPI-Filter','Web-Includes','Web-WebSockets',
'Web-Ftp-Server','Web-Ftp-Service','Web-Mgmt-Tools','Web-Mgmt-Console','Web-Mgmt-Compat','Web-Metabase','Web-Lgcy-Mgmt-Console','Web-Lgcy-Scripting',
'Web-WMI','Web-Scripting-Tools','Web-Mgmt-Service','NET-Framework-Features','NET-Framework-Core','NET-Framework-45-Features','NET-Framework-45-Core',
'NET-Framework-45-ASPNET','NET-WCF-Services45','NET-WCF-HTTP-Activation45','NET-WCF-TCP-PortSharing45','RSAT','RSAT-Feature-Tools','RSAT-SMTP',
'RSAT-SNMP','FS-SMB1','SMTP-Server','SNMP-Service','User-Interfaces-Infra','Server-Gui-Mgmt-Infra','Server-Gui-Shell','PowerShellRoot','PowerShell',
'PowerShell-V2','PowerShell-ISE','WAS','WAS-Process-Model','WAS-Config-APIs','WoW64-Support')
# Remove any feature that is not available to prevent dism failures. MSMQ-Container for example is only available
# on Windows 7 but not on Windows Server 2008 R2.
$availableFeatures = dism /online /Get-Features
$featuresToRemove = #()
$featuresToInstall | % { if (-not ($availableFeatures | Select-String ('{0}$' -f $_) -Quiet)) { $featuresToRemove += $_} }
$featuresToInstall = Compare-Object -ReferenceObject $featuresToInstall -DifferenceObject $featuresToRemove | select -ExpandProperty InputObject
$dismParameter = #('/online', '/Enable-Feature', ($featuresToInstall | % { '/FeatureName:{0}' -f $_ }), '/NoRestart', '/all')
$output = dism #dismParameter
# throw an error if dism wasn't successful
if ($global:LastExitCode -ne 0)
{
throw 'Error while installing Windows Features. {0}' -f ($output | Select-String '\.log$')
}
The Script also validates whether the feature is available to install using dism and removes them in that case (you can check $featuresToRemove).
In a non core installation of Windows server 2 features are not present, one of them is net-framework-core (i forgot which other one it is), thats why you point to the source in the wim like the MS kb states, but this fails most of the times.
Instead of pointing the source to the wimfile you should point it to the \sources\sxs location of your installation medium, this always does the trick for me (you can also point to a network location with the files)

Powershell script cannot get applications list data from windows 7 machine

Recently, I made a script to list all the installed applications in local & remote machine & give the output in a structured manner in an excelsheet.
It looks like this:
$a = Read-Host "Enter machine name" | Out-File -filepath C:\machine.txt
$computerName = Get-Content C:\machine.txt
$a = New-Object -comobject Excel.Application
$a.visible = $True
$b = $a.Workbooks.Add()
$c = $b.Worksheets.Item(1)
$c.Cells.Item(1,1) = "Name"
$c.Cells.Item(1,2) = "Publisher"
$c.Cells.Item(1,3) = "InstalledDate"
$c.Cells.Item(1,4) = "Version"
$c.Cells.Item(1,5) = "UninstallString"
$d = $c.UsedRange
$d.Interior.ColorIndex = 19
$d.Font.ColorIndex = 11
$d.Font.Bold = $True
$i = 2
function Get-InstalledAppReg ([string]$ComputerName) {
$RegPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
$BaseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine", $ComputerName)
$OpenSubKey = $BaseKey.OpenSubKey($RegPath)
$i =2
$OpenSubKey.GetSubKeyNames() | ForEach {
$Path = "$RegPath\$_"
$BaseKey.OpenSubKey($Path).GetValue("DisplayName")
$BaseKey.OpenSubKey($Path).GetValue("Publisher")
$BaseKey.OpenSubKey($Path).GetValue("InstalledDate")
$BaseKey.OpenSubKey($Path).GetValue("Version")
$BaseKey.OpenSubKey($Path).GetValue("UninstallString")
$c.Cells.Item($i,1) = $BaseKey.OpenSubKey($Path).GetValue("DisplayName")
$c.Cells.Item($i,2) = $BaseKey.OpenSubKey($Path).GetValue("Publisher")
$c.Cells.Item($i,3) = $BaseKey.OpenSubKey($Path).GetValue("InstalledDate")
$c.Cells.Item($i,4) = $BaseKey.OpenSubKey($Path).GetValue("Version")
$c.Cells.Item($i,5) = $BaseKey.OpenSubKey($Path).GetValue("UninstallString")
$i ++
}
}
Get-InstalledAppReg($computerName)
$d.EntireColumn.AutoFit()
$b.SaveAs("c:\softhive.xlsx")
$b.Close()
$a.Quit()
Get-Process | Where { $_.Name -Eq "Excel" } | Kill
This script ran perfectly for all remote machines which has XP as a OS.
Problem started when I started running it in windows & machines remotely.
Initially it gave wrong path error, when I realized that for windows 7, I probably have to use
"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" instead of
"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall".
With this different path, when I run the same script again, I get an error:
Exception calling "OpenRemoteBaseKey" with "2" argument(s): "The network path was not found.
"
At :line:24 char:62
$BaseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey( <<<< "LocalMachine", $ComputerName)
Probably, I need to change other things too in the script?
My machine, from where I run the script, is a windows XP SP3 machine.
Unfortunately the WMI Win32_Product class does not report all apps found in Control Panel's "Add or Remove Programs"...
The registry walk seems to be unavoidable, see:
http://powergui.org/thread.jspa?threadID=17068
http://learningpcs.blogspot.fr/2011/10/powershell-get-installed-software.html
Rather than comb the registry, I would use WMI for this. See Win32_Product and friends e.g.:
Get-WmiObject Win32_Product
Note that if I run this on my Windows 7 x64 system in a 64bit PowerShell prompt it shows all installed apps (32-bit and 64-bit):
Get-WmiObject Win32_Product| sort Vendor | Format-Table Name,InstallDate,Vendor
To see all the properties available execute:
Get-WmiObject Win32_Product | Select -First 1 | Format-List *
I remember a while back I did something like this at an IT firm and we simply searched the C: directory for the names of all programs ending in .exe, in order to optimize we would hone in on specific apps that we were looking for. We set up a batch that would pass or fail based on if what we wanted. Keep in mind this is a batch file, however the idea is similar.
echo ================= >>Software_Scan.txt
echo Below is a list of all wireless networks. Saved networks will be found in the Wireless Profiles folder
set filePath=
for /R "C:\Program Files (x86)" /D %%a in (*) do if exist "%%a\YahooMessenger.exe" set filePath=%%a& goto continue
:continue
if defined filePath echo %COMPUTERNAME% FAIL Yahoo Messenger >> Software_Scan.txt
if NOT defined filePath echo %COMPUTERNAME% PASS Yahoo Messenger >> Software_Scan.txt