Trying to use Kentico with powershell - powershell

I'm new to using Kentico and I've been given a task to write a script with it using powershell. I'm having a little trouble getting started. I'm following "PowerShell Calling Reflected Kentico API" to try to get it to work. In particular, I tried:
# Configure path to the root of your web project
$webRoot = "C:\inetpub\...\CMS\"
$bin = $webRoot + "bin\"
# Load settings from web.config
[System.AppDomain]::CurrentDomain.SetData("APP_CONFIG_FILE", $webRoot + "web.config")
Add-Type -AssemblyName System.Configuration
[Configuration.ConfigurationManager].GetField("s_initState", "NonPublic, Static").SetValue($null, 0)
[Configuration.ConfigurationManager].GetField("s_configSystem", "NonPublic, Static").SetValue($null, $null)
([Configuration.ConfigurationManager].Assembly.GetTypes() | where {$_.FullName -eq "System.Configuration.ClientConfigPaths"}) [0].GetField("s_current", "NonPublic, Static").SetValue($null, $null)
# Add DLL resolution path
[System.AppDomain]::CurrentDomain.AppendPrivatePath($bin);
# Load CMSDependencies
Get-ChildItem -recurse "$webRoot\CMSDependencies\"|Where-Object {($_.Extension -EQ ".dll")} | ForEach-Object { $AssemblyName=$_.FullName; Try {Add-Type -Path $AssemblyName} Catch{ "Failed to load assembly: " + $AssemblyName + " " + $_.Exception.LoaderExceptions}}
# Load all assemblies from \bin\ (to be sure that all handlers get attached etc.)
Get-ChildItem -recurse "$bin"|Where-Object {($_.Extension -EQ ".dll")} | ForEach-Object { $AssemblyName=$_.FullName; Try {Add-Type -Path $AssemblyName} Catch{ "Failed to load assembly: " + $AssemblyName + " " + $_.Exception.LoaderExceptions}}
# Optionally, replace the above with loading only the minimum set of assemblies
#$references = #(("CMS.Base.dll"),("CMS.DataEngine.dll"), ("CMS.DataProviderSQL.dll"),("CMS.Membership.dll")) | Foreach-Object{ $bin + $_ }
#Add-Type -Path $references
# If the API you are going to use is about to touch the file system set the web application root path
#[CMS.Base.SystemContext]::WebApplicationPhysicalPath = $webRoot
# Initialize application
"Application initialized:" + [CMS.DataEngine.CMSApplication]::Init();
But I'm getting an error on [CMS.DataEngine.CMSApplication]::Init():
Exception calling "Init" with "0" argument(s): "Login failed for user ..."
What might be the issue with the login?

I think that this is an error with your SQL connection. Check your web.config file to see if you're using trusted authentication in the connection string (CMSConectionString). If you're using trusted, it may simply be that the user that you're using to run PowerShell does not have the correct SQL permissions.

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
}

Create Receive Location and Send Port in Existing BizTalk Application using Powershell

I am needing to create receive locations and send ports in an already existing BizTalk application using Powershell. I have only seen some documentation on how to create an application but not to call upon one. Any suggestions would be beneficial. There are some things that are commented out, and that is because I cannot disclose that information. I added at the last part on what I have learned of how to create an application, but that is not something that I want for my script. The program below is what I have so far:
#===Create a receive port and location function===#
Function CreateRPandRL ()
{
#Creating Receive Port
$myReceivePort = $catalog.AddNewReceivePort($false)
$myReceivePort.Name = "My Receive Port"
#Creating Receive Location
$myReceiveLocation = $myReceivePort.AddNewReceiveLocation()
foreach ($handler in $catalog.ReceiveHandlers)
{
if ($handler.TransportType.Name -eq "FILE")
{
$myReceiveLocation.ReceiveHandler = $handler
break
}
}
#Associate a transport protocol and file location with receive location
$myReceiveLocation.TransportType = $catalog.ProtocolTypes["FILE"]
$myReceiveLocation.Address = #pick-up file location
#Assign the first receive pipeline found to process the message
foreach ($pipeline in $catalog.Pipelines)
{
if ($pipeline.Type -eq [Microsoft.BizTalk.ExplorerOM.PipelineType] "File_Receive")
{
$myReceiveLocation.ReceivePipeline = $pipeline
break
}
#Enable the receive location
$myReceiveLocation.Enable = $true
}
#Try to commit the changes made so far. If the commit fails, roll back changes
$catalog.SaveChanges()
}
Function CreateSendPorts($Catalog)
{
#=== Register a trap handler to discard changes on exceptions ===#
$ErrorActionPreference="silentlycontinue"
trap { "Exception encountered:`r`n"; $_; "`r`nDiscarding Changes.`r`n";$Catalog.DiscardChanges();exit; }
#=== create a new static one-way send port using FILE transport ===#
$mySendPort = $Catalog.AddNewSendPort($false,$false)
$mySendPort.Name = "My Send Port"
$mySendPort.PrimaryTransport.TransportType = $catalog.ProtocolTypes["FILE"]
$mySendPort.PrimaryTransport.Address = #drop-off file location
$mySendPort.SendPipeline = $Catalog.Pipelines["Microsoft.BizTalk.DefaultPipelines.EdiSend"]
#=== Persist new ports to BizTalk configuration database ===#
Write-Host "Adding $mySendPort.Name..."
$catalog.SaveChanges();
Write-Host "`r`n $mySendPort.Name has been created."
#=== specify filters for content-based routing ===#
Write-Host $mySendPort.Name: Adding a filter
$mySendPort.Filter = "<Filter><Group>" +
"<Statement Property='EDI.ISA06' Operator='0' Value='9999999999'/>" +
"<Statement Property='EDI.ST01' Operator='0' Value='999'/>" +
"<Statement Property='EDI.IsSystemGeneratedAck' Operator='0' Value='true'/>" +
"<Statement Property='BTS.ReceivePortName' Operator='0' Value= $myReceivePort.Name/>" +
"</Group></Filter>"
#=== Persist all changes to BizTalk configuration database ===#
Write-Host $mySendPort.Name + ": Saving changes"
$catalog.SaveChanges();
Write-Host "`r`nFilters for $mySendPort.Name created"
#===========Changing Send Port status===========#
#Register a trap handler to discard changes on exceptions
$ErrorActionPreference="silentlycontinue"
trap { "Exception encountered:`r`n"; $_; "`r`nDiscarding Changes.`r`n";$Catalog.DiscardChanges();exit; }
#start the send port to begin processing messages
$mySendPort.Status = [Microsoft.BizTalk.ExplorerOM.PortStatus] "Started"
Write-Host "Changing" + $mySendPort.Name + "status to ($mySendPort.Status)..."
$catalog.SaveChanges()
Write-Host "Complete."
}
#===Main Script===#
#make sure the ExplorerOM assembly is loaded
[void][System.reflection.Assembly]::LoadwithPartialName("Microsoft.BizTalk.ExplorerOM")
#Connect to the BizTalk management database
$Catalog = New-Object Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer
$Catalog.ConnectionString = "SERVER = #server_address; DATABASE=BizTalkMgmtDB; Integrated Security=SSPI"
#Implementing functions
CreateRPandRL
CreateSendPorts
Start-Sleep -Seconds 60
<#
#===BizTalk Application===#
$app = $Catalog.AddNewApplication()
$app.Name = "TestingPowershellScript"
$app.CreateRPandRL()
$app.CreateSendPorts()
#>
Hoo boy, this takes me back a few years, I'm glad I'm not the only one to struggle with this. You want to leave that alone and switch to the BizTalk PowerShell Extensions (information on this is sketchy), they are sooooooo much easier to work with in PowerShell.
I cobbled this together from some scripts I used, and left out some of the fancy stuff, but what you want is basically:
$InitializeDefaultBTSDrive = $false
Import-Module "$env:BTSINSTALLPATH\SDK\Utilities\PowerShell\BizTalkFactory.PowerShell.Extensions.dll" -WarningAction Ignore
New-PSDrive -Name BizTalk -PSProvider BizTalk -Root BizTalk:\ -Instance $DatabaseName -Database $BizTalkMgmtDb
This opens up a whole world of goodies, because it's loaded as a PSDrive, you can navigate round it, create things, delete things, use it all as native as any other drive/filesystem, such as:
Get-ChildItem "BizTalk:\All Artifacts\Receive Locations"
Get-ChildItem "BizTalk:\All Artifacts\Receive Locations" | Disable-ReceiveLocation
Get-ChildItem "BizTalk:\Platform Settings\Host Instances" | Stop-HostInstance
Get-ChildItem "BizTalk:\Platform Settings\Host Instances" | Where-Object { $_.IsDisabled -eq $false } | Start-HostInstance
Get-ChildItem "BizTalk:\All Artifacts\Receive Locations" | Enable-ReceiveLocation
Get-ChildItem -Path "BizTalk:\Health and Activity\Service Instances"
There's so much more than the above, and none of this is what you really asked for, what you actually want is:
Import-Bindings -Path "BizTalk:" -Source $bindings
Where $bindings is your XML bindings file.
My advice, don't even try this. Most of the useful settings for Adapters are not exposed by any API so this will get you maybe half way at most.
Instead, script the import of a binding file which does support all settings for all Adapters.

Visual studio Build Task Issue for PowerShell inline task - Azure

I am running a vsts build inline PowerShell script task to create package for Azure cloud service. It works fine and create package file from my local machine, but when I try to run from VSTS PowerShell inline task it gives error :
##[error]Cannot find path ‘D:\a_tasks\InlinePowershell_31f040e5-e040-4336-878a-59a493355534\1.1.6\ServiceConfiguration.Cloud.Test.cscfg’ because it does not exist.
Here is my PowerShell inline script below, It fails on the following line:
Copy-Item $serviceConfigurationPath $packageOutDir
I really appreciate your help on this.
Thanks,
# This is the VSTS repository path
$workingDirectory = “$/DevCodeBase/ToolDevBranch1.33”
$webProjectName = “WebRole1”
$cloudProjectName = ‘ProjAzureDeployment’
$evv =’Test’
$cppack = ‘C:\Program Files\Microsoft SDKs\Azure\.NET SDK\v2.9\bin\cspack.exe’
$solutionDir = [string]::Format(“{0}”, $workingDirectory)
$webDir = [string]::Format(“{0}\{1}”, $workingDirectory, $webProjectName)
$packageOutDir = [string]::Format(“{0}\{1}”, $workingDirectory, $cloudProjectName)
$rolePropertyFile = [string]::Format(“{0}\{1}\{2}”, $workingDirectory, $cloudProjectName, “roleproperties.txt”)
# Create Role Properties File – This property file specifies the .Net framework against which webrole is going to run.
New-Item $rolePropertyFile -Type file -Force -Value “TargetFrameWorkVersion=v4.5” | Out-Null
New-Item $packageOutDir -Type directory -Force | Out-Null
# CSPack command Definition
$serviceDefinitionPath = [string]::Format(“{0}\{1}\ServiceDefinition.csdef”, $solutionDir, $cloudProjectName)
if ($evv -eq “Test”){
$serviceConfigurationPath = “ServiceConfiguration.Cloud.Test.cscfg”
}
else
{
$serviceConfigurationPath = [string]::Format(“{0}\{1}\ServiceConfiguration.Cloud.cscfg”, $solutionDir, $cloudProjectName)
}
$serviceRole = [string]::Format(“/role:{0};{1}”, $webProjectName, $webDir)
$rolePropertiesFile = [string]::Format(“/rolePropertiesFile:{0};{1}”, $webProjectName, $rolePropertyFile)
$sites = [string]::Format(“/sites:{0};Web;{1}”, $webProjectName, $webDir)
$packageOutput = [string]::Format(“/out:{0}\{1}.cspkg”, $packageOutDir, $cloudProjectName)
# $packageOutput = [string]::Format(“{0}\{1}.cspkg”, $packageOutDir, $cloudProjectName)
Write-Host $packageOutput
Write-Host $serviceConfigurationPath
# Build CSPKG file
& “C:\Program Files\Microsoft SDKs\Azure\.NET SDK\v2.9\bin\cspack.exe” $serviceDefinitionPath $serviceRole $rolePropertiesFile $sites $packageOutput /useCtpPackageFormat | Out-Null
Write-Host $serviceDefinitionPath
Write-Host $serviceRole
Write-Host $rolePropertiesFile
Write-Host $sites
Write-Host $packageOutput
Write-Host ‘before copy’
# Copy configuration file
Copy-Item $serviceConfigurationPath $packageOutDir
# Remove Role Properties File
Remove-Item -Path $rolePropertyFile -Force | Out-Null
In the VSTS task you'll have to specify an absolute path, otherwise the script will look in the temporary directory created for your inline powershell script.
For instance, you could supply the path to the file as a parameter like
-filepath "$(System.DefaultWorkingDirectory)\Solution\config.json"
(For a list of the variables you can use, have a peek here)
If you want to keep using a relative path, you can move to a file based (ie non-inline) script and use a relative path to that.

query on powershell script that downloads the Microsoft Ebook Giveaway books

Got a whatsapp message that Microsoft is giving away free ebooks from the below url.
URL : Microsoft Ebook Giveaway
To download all the books in one go, the following powershell script was used, which is available in the same url.
Now my problem is, if I run the powershell script as a whole, it is not throwing any error. All the books from the url gets downloaded to a single location in my computer.
But if I try to run the script line by line to understand what each statement does, it is giving the following error when the , $bookList = Invoke-WebRequest $downLoadList gets executed,
Now to resolve this error, there are many others posts in stack overflow, that passes the username and password to overcome this error. Those scripts / solutions are not working at my end.
More than the error, why is it, that the script runs without any errors/issues when I execute the full script, but throws an error when I execute line by line ?
Any inputs on the nature of execution or helpful tips in overcoming the error will be useful... Thank you.
Error :
Invoke-WebRequest : (my ip number )
Credentials are missing.
Make sure to specify a domain with your username
This website has been blocked by a cyber security policy
and SecureWeb does not currently support web exceptions
If you have an exception, copy the link below into a new tab
http://ligman.me/2tk1D2V
At line:1 char:13
+ $bookList = Invoke-WebRequest $downLoadList
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest)
[Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.
InvokeWebRequestCommand
PowerShell Script :
###############################################################
# Eric Ligmans Amazing Free Microsoft eBook Giveaway
# https://blogs.msdn.microsoft.com/mssmallbiz/2017/07/11/largest-free-microsoft-ebook-giveaway-im-giving-away-millions-of-free-microsoft-ebooks-again-including-windows-10-office-365-office-2016-power-bi-azure-windows-8-1-office-2013-sharepo/
# Link to download list of eBooks
# http://ligman.me/2tk1D2V
# Thanks David Crosby for the template (https://social.technet.microsoft.com/profile/david%20crosby/)
#
# Modified by Robert Cain (http://arcanecode.me)
# Added code to check to see if a book was already downloaded,
# and if so was it the correct file size. If so, the book
# download is skipped. This allows users to simply rerun the
# script if their download process is interrupted.
###############################################################
# Set the folder where you want to save the books to
$dest = "I:\new_microsoft\" # Make sure the file path ends in a \
# Download the source list of books
$downLoadList = "http://ligman.me/2tk1D2V"
$bookList = Invoke-WebRequest $downLoadList
# Convert the list to an array
[string[]]$books = ""
$books = $bookList.Content.Split("`n")
# Remove the first line - it's not a book
$books = $books[1..($books.Length -1)]
$books # Here's the list
# Get the total number of books we need to download
$bookCount = $($books).Count
# Set a simple counter to let the user know what book
# number we're currently downloading
$currentBook = 0
# As an option, we can have it log progress to a file
$log = $true
if ($log -eq $true)
{
# Construct a log file name based on the date that
# we can save progress to
$dlStart = Get-Date
$dlStartDate = "$($dlStart.Year)-$($dlStart.Month)-$($dlStart.Day)"
$dlStartTime = "$($dlStart.Hour)-$($dlStart.Minute)-$($dlStart.Second)"
$logFile = "$($dest)BookDlLog-$dlStartDate-$dlStartTime.txt"
}
# Download the books
foreach ($book in $books)
{
# Increment current book number
$currentBook++
try
{
# Grab the header with the books full info
$hdr = Invoke-WebRequest $book -Method Head
# Get the title of the book from the header then
# make it a safe string (remove special characters)
$title = $hdr.BaseResponse.ResponseUri.Segments[-1]
$title = [uri]::UnescapeDataString($title)
# Construct the path to save the file to
$saveTo = $dest + $title
# If the file doesn't exist, download it
if ($(Test-Path $saveTo) -eq $false)
{
$msg = "Downloading book $currentBook of $bookCount - $title"
$msg
if ($log -eq $true) { "`n$($msg)" | Add-Content $logFile }
Invoke-WebRequest $book -OutFile $saveTo
}
else
{
# If it does exist, we need to make sure it wasn't
# a partial download. If the file size on the server
# and the file size on local disk don't match,
# redownload it
# Get the size of the file from the download site
$dlSize = $hdr.BaseResponse.ContentLength
# Get the size of the file on disk
$fileSize = $(Get-ChildItem $saveTo).Length
if ($dlSize -ne $fileSize)
{
# If not equal we need to download the book again
$msg = "Redownloading book $currentBook of $bookCount - $title"
$msg
if ($log -eq $true) { "`n$($msg)" | Add-Content $logFile }
Invoke-WebRequest $book -OutFile $saveTo
}
else
{
# Otherwise we have a good copy of the book, just
# let the user know we're skipping it.
$msg = "Book $currentBook of $bookCount ($title) already exists, skipping it"
$msg
if ($log -eq $true) { "`n$($msg)" | Add-Content $logFile }
}
}
} # end try
catch
{
$msg = "There was an error downloading $title. You may wish to try to download this book manually."
Write-Host $msg -ForegroundColor Red
if ($log -eq $true) { "`n$($msg)" | Add-Content $logFile }
} # end catch
} # end foreach
# Let user know we're done, and give a happy little beep
# in case they aren't looking at the screen.
#"Done downloading all books"
#[Console]::Beep(500,300)

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]
}