How do I deduce path to Azure SDK csrun.exe conveniently? - windows

I have some problems with Azure Compute Emulator not restarting properly. To resolve this I want to add csrun /devfabric:stop call to a pre-build step in Visual Studio solution.
The problem is csrun.exe is located in C:\Program Files\Windows Azure SDK\v1.4\bin on my machine and that path is not on the %PATH% directories list. I don't want to hardcode that path in my solution.
Is there some way to deduce the path like using some environment variable or something similar?

You can read the Azure SDK path from the registry by version. The last part of the path is the version ... Your code can either be set to a version or you can iterate over the v keys finding the latest. I would recommend having a constant for the version you support and as you take a new SDK as a pre-req.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\ServiceHosting\v1.4
There's an "InstallPath" key under those paths.

I had this same problem and I produced a PowerShell script that sets an environment variable with the path to the SDK bin folder. It will automatically search the registry and find the latest installed version. It also has a fallback to the alternate registry location, depending whether your script runs in 32bit or 64bit mode. Hope it helps!
Disclaimer: I removed some stuff from the script before posting it here and I didn't test it afterwards but I think it's not difficult to debug/adjust it to your needs.
#the script attempts to perform the following:
#1. look for the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\ServiceHosting" registry key
#2. if the above key is present then read the child keys and retrieve the largest version number
#3. from the largest version number key retrieve the "InstallPath" string value to determine the path of the latest Azure SDK installation
#4. add an environment variable called "AzureSDKBin" (if not already added) with the path to the "bin" folder of the latest Azure SDK installation
#define the name of the config variable
$azureSDKPathVariable = 'AzureSDKBin'
$azureRegistryKey = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\ServiceHosting'
$azureAlternateRegistryKey = 'HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\ServiceHosting' #this is in case the PowerShell runs in 32bit mode on a 64bit machine
$azureMatchedKey = ''
#check if the environment variable was already defined
if ([environment]::GetEnvironmentVariable($azureSDKPathVariable,"User").Length -eq 0) {
'Variable ' + $azureSDKPathVariable + ' is not defined, proceeding...'
#try reading the registry key
$keyExists = Get-Item -Path Registry::$azureRegistryKey -ErrorAction SilentlyContinue
$azureMatchedKey = $azureRegistryKey #make a note that we found this registry key
#stop if the key does not exist
if ($keyExists.Length -eq 0) {
'Could not find registry key in primary location: ' + $azureRegistryKey + ', attempting search in alternate location: ' + $azureAlternateRegistryKey
#search the alternate location
$keyExists = Get-Item -Path Registry::$azureAlternateRegistryKey -ErrorAction SilentlyContinue
$azureMatchedKey = $azureAlternateRegistryKey #make a note that we found this registry key
if ($keyExists.Length -eq 0) {
'Could not find registry key for determining Azure SDK installation: ' + $azureAlternateRegistryKey
'Script failed...'
exit 1
}
}
'Found Azure SDK registry key: ' + $azureMatchedKey
#logic for determining the install path of the latest Azure installation
#1. get all child keys of the matched key
#2. filter only keys that start with "v" (e.g. "v2.2", "v2.3")
#3. sort the results by the "PSChildName" property from which we removed the starting "v" (i.e. only the version number), descending so we get the latest on the first position
#4. only keep the first object
#5. read the value named "InstallPath" under this object
$installPath = (Get-ChildItem -Path Registry::$azureMatchedKey | Where-Object { $_.PSChildName.StartsWith("v") } | sort #{expression={ $_.PSChildName.TrimStart("v") }} -descending | Select-Object -first 1| Get-ItemProperty -name InstallPath).InstallPath
'Detected this Azure SDK installation path: "' + $installPath + '"'
#set the variable with the "bin" folder
[Environment]::SetEnvironmentVariable($azureSDKPathVariable, $installPath + 'bin\', "User")
'Assigned the value "' + [environment]::GetEnvironmentVariable($azureSDKPathVariable,"User") + '" to environment variable "' + $azureSDKPathVariable + '"'
}
else {
'Environment variable "' + $azureSDKPathVariable + '" is already defined and has a value of "' + [environment]::GetEnvironmentVariable($azureSDKPathVariable,"User") + '"'
}

Related

Configure Microsoft Edge with powershell - Windows 11

My goal is to configure Microsoft Edge with Powershell only.
So for example, the first window does not appear when you open it for the first time, to adjust the overlay etc. .
Set the home page, show the home button at the top, set favorites, etc.
What is the best way to do this with Powershell?
The logic to configuring MS Edge with PowerShell is you need to access and modify the relevant registry keys using PowerShell.
For configuring different Edge browser features, you need to configure the corresponding registries. so you would need to know which registry key is used for which feature.
Below is an example to set the Homepage for the Edge browser using Powershell.
# Ensure Edge key exists
$EdgeHome = 'HKCU:\Software\Policies\Microsoft\Edge'
If ( -Not (Test-Path $EdgeHome)) {
New-Item -Path $EdgeHome | Out-Null
}
# Set RestoreOnStartup value entry
$IPHT = #{
Path = $EdgeHome
Name = 'RestoreOnStartup'
Value = 4
Type = 'DWORD'
}
Set-ItemProperty #IPHT -verbose
# Create Startup URL's registry key
$EdgeSUURL = "$EdgeHome\RestoreOnStartupURLs"
If ( -Not (Test-Path $EdgeSUURL)) {
New-Item -Path $EdgeSUURL | Out-Null
}
# Create a single URL startup page
$HOMEURL = 'https://duckduckgo.com'
Set-ItemProperty -Path $EdgeSUURL -Name '1' -Value $HomeURL
Helpful References:
How to Change the Start Page for the Edge Browser
How can I set the home page in Edge Chromium with Powershell?
The above example will give you an idea about how to access and modify the registry using PowerShell to configure the Edge features.
Further, you could make tests on your side to configure other options for the Edge browser.
Note: Some Edge options/ features are only available to configure if the machine is AD joined. If you try to configure and the machine is not AD joined then it will be ignored.

How to query and add (if not listed) a location to Windows 10 Search Index using PowerShell

I have to re-mount removable drives (which require authentication) each time I boot the computer and Windows Indexing keeps removing the removable drives (perhaps because the removable drives are not available when the computer boots). In an ideal world Windows Indexing would keep these locations and just list them as 'Unavailable' (which it sometimes does). However because it doesn't I am interested in executing a script that queries the Windows Indexing locations and if it does not list the removable drives then add them. At the bottom of this thread I pasted the Batch script that I setup to run at boot (via Start Up folder) to search for a specific folder that is available thereafter mounting one of the removable drives.
I have found several examples of how to do this on Windows 7 (links pasted below) but I can't figure out how to do it in Windows 10. The links provided to the DLL (Microsoft.Search.Interop.dll) no longer resolve.
When searching for the latest Windows Search SDK for Windows 10 I was lead to the Windows SDK here:
https://learn.microsoft.com/en-us/windows/win32/search/-search-developers-guide-entry-page
I installed the C++ related portion of the Windows SDK then searched for Microsoft.Search.Interop.dll but I couldn't find it. Perhaps the DLL has changed?
From How to rebuild Windows Search Index by using PowerShell?
Load DLL containing classes & interfaces
Add-Type -path "C:\Temp\SearchIndexSdk\Microsoft.Search.Interop.dll"
#Provides methods for controlling the Search service. This
interface manages settings and objects that affect the search engine
across catalogs.
https://msdn.microsoft.com/en-us/library/bb231485(v=vs.85).aspx
$sm = New-Object Microsoft.Search.Interop.CSearchManagerClass
#Retrieves a catalog by name and creates a new ISearchCatalogManager
object for that catalog.
$catalog = $sm.GetCatalog("SystemIndex")
#Resets the underlying catalog by rebuilding the databases and performing a full indexing.
#https://msdn.microsoft.com/en-us/library/bb266414(v=vs.85).aspx
$catalog.Reset()
From How to add a location to windows 7/8 search index using batch or vbscript?
#Code copied from "Powershell Tackles Windows Desktop Search" http://powertoe.wordpress.com/2010/05/17/powershell-tackles-windows-desktop-search/
#Microsoft.Search.Interop.dll is needed, download from http://www.microsoft.com/en-us/download/details.aspx?id=7388
#Load the dll
Add-Type -path "D:\Unattend\UserFiles\Tools\Microsoft.Search.Interop.dll"
#Create an instance of CSearchManagerClass
$sm = New-Object Microsoft.Search.Interop.CSearchManagerClass
#Next we connect to the SystemIndex catalog
$catalog = $sm.GetCatalog("SystemIndex")
#Get the interface to the scope rule manager
$crawlman = $catalog.GetCrawlScopeManager()
#add scope
$crawlman.AddUserScopeRule("file:///D:*",$true,$false,$null)
$crawlman.SaveAll()
I would add a comment to the existing threads but I am not able to because I don't have reputation of 50 (dumb rule IMO).
Last... I found this site which lists the DLL along with some code but it hasn't been updated in a long time.
https://github.com/FileMeta/WindowsSearchSample
Thanks in advance!
Batch script that runs at boot:
#echo off
echo Windows Search is being restarted to recognize the Z drive
:while
if EXIST Z:\Watch (
I WANT TO CALL POWERSHELL SCRIPT TO ADD THE LOCATION TO THE INDEX IF NEEDED HERE
sc stop WMPNetworkSvc
ping 127.0.0.1 -n 5 > nul
sc stop WSearch
ping 127.0.0.1 -n 5 > nul
sc start WSearch
ping 127.0.0.1 -n 5 > nul
sc start WMPNetworkSvc
echo Exiting this script in 5 seconds
ping 127.0.0.1 -n 5 > nul
exit
) else (
echo Waiting 60 seconds to check if Z drive is available
ping 127.0.0.1 -n 60 > nul
goto :while
)
When I do a search for Searchdll in what I believe to be the folder where the Windows SDK installed to (C:\Program Files (x86)\Windows Kits\10) I find the following. If I had to guess which DLL is the Windows 10 equivalent of Windows 7's Microsoft.Search.Interop.dll I would guess that it's the 1st one i.e. interop.searchapi.dll.
Add-Type -Path "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\interop.searchapi.dll" does return without error... however $sm = New-Object Microsoft.Search.Interop.CSearchManagerClass returns with error that it cannot find the class in the assembly.
When I cd to "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64" and enter ([appdomain]::currentdomain.GetAssemblies() | Where-Object Location -Match 'interop.searchapi').gettypes() I get the following
When I enter (([appdomain]::currentdomain.GetAssemblies() | Where-Object location -match 'interop.searchapi.dll').gettypes() | Where-Object name -eq 'CSearchManagerClass').getmembers() | Format-Table name, membertype I get
From the list of commands in the previous threads I do see GetCatalog and I presume that the members GetCrawlScopeManager, AddUserScopeRule, Reset, and SaveAll exist.
I don't know how to find the fully qualified class name or I'm doing something else wrong (unknowingly).
When I enter ([appdomain]::currentdomain.GetAssemblies() | Where-Object Location -Match 'interop.searchapi').fullname I get the following
Interop.SearchAPI, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
But when I enter $sm = New-Object Interop.SearchAPI.CSearchManagerClass I get an error that it can't find the type Interop.SearchAPI.CSearchManagerClass.

How to get the Dropbox folder in Powershell in Windows

Same question exists for Python here: How can I get the Dropbox folder location programmatically in Python?, or here for OSX: How to get the location of currently logined Dropbox folder
Same thing in Powershell. I need the path of DropBox to copy files to it (building a software and then copying it to dropbox to share with team).
This Dropbox help page tells us where this info is stored, ie, in a json file in the AppData of the user: https://www.dropbox.com/help/4584
function GetDropBoxPathFromInfoJson
{
$DropboxPath = Get-Content "$ENV:LOCALAPPDATA\Dropbox\info.json" -ErrorAction Stop | ConvertFrom-Json | % 'personal' | % 'path'
return $DropboxPath
}
The line above is taken from: https://www.powershellgallery.com/packages/Spizzi.Profile/1.0.0/Content/Functions%5CProfile%5CInstall-ProfileEnvironment.ps1
Note that it doesn't check if you've got a Dropbox business account, or if you have both. It just uses the personal one.
You can then use this base Dropbox folder to build your final path, for example:
$targetPath = Join-Path -Path (GetDropBoxPathFromInfoJson) -ChildPath 'RootDropboxFolder\Subfolder1\Subfolder2'
if (-not (Test-Path -Path $targetPath)) { throw "Path '$targetPath' not found!" }
--
Alternative way is using the host.db file, as shown on this page:
http://bradinscoe.tumblr.com/post/75819881755/get-dropbox-path-in-powershell
$base64path = gc $env:appdata\Dropbox\host.db | select -index 1 # -index 1 is the 2nd line in the file
$dropboxPath = [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($base64path)) # convert from base64 to ascii

New-WebBinding: Cannot retrieve the dynamic parameters for the cmdlet

We're using Windows 2012 Server R2.
We're trying to automate the creation of LetsEncrypt certificates. We're using LetsEncrypt-Win-Simple (https://github.com/Lone-Coder/letsencrypt-win-simple).
Once the cert is created (via LetsEncrypt.exe) we have a .bat script that gets called (using the --script and --scriptparameters flags). This runs powershell.exe and tries to create the necessary IIS binding. The line in the .bat file is:
powershell.exe -file c:\temp\SSLIISBinding.ps1 %1 %2 %3 %4
The %1-4 are args passed in by LetsEncrypt. In the powershell script, the command we're trying to run is:
$iis_host_name = $args[0]
$iis_site_name = $args[1]
$certificate_hash = $args[2]
$certificate_store = $args[3]
"IIS Host Name: " + $iis_host_name
"IIS Site Name: " + $iis_site_name
"Certificate Hash: " + $certificate_hash
"Certificate Store: " + $certificate_store
$guid = [guid]::NewGuid().ToString("B")
netsh http add sslcert hostnameport="${iis_host_name}:443" certhash=$certificate_hash certstorename=$certificate_store appid="$guid"
New-WebBinding -name $iis_site_name -Protocol https -HostHeader $iis_host_name -Port 443 -SslFlags 1
The args are passed into the .bat fine, as we output them and they are showing correctly.
If we run the .bat file on its own, it works perfectly. If it gets called by LetsEncrypt.exe it fails, reporting the following issue:
New-WebBinding : Cannot retrieve the dynamic parameters for the cmdlet.
Retrieving the COM class factory for component with CLSID
{688EEEE5-6A7E-422F-B2E1-6AF00DC944A6} failed due to the following error:
80040154 Class not registered (Exception from HRESULT: 0x80040154
(REGDB_E_CLASSNOTREG)).
At C:\temp\SSLIISBinding.ps1:13 char:1
+ New-WebBinding -name $iis_site_name -Protocol https -HostHeader
$iis_host_name ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~
+ CategoryInfo : InvalidArgument: (:) [New-WebBinding], Parameter
BindingException
+ FullyQualifiedErrorId : GetDynamicParametersException,Microsoft.IIs.Powe
rShell.Provider.NewWebBindingCommand
I've googled, some mentioning something about 32bit vs 64bit powershell, but I've tried using all the different powershell.exe available.
Anyone hit this issue, or know to resolve.
If we call .bat directly from command line it works fine, just as part of being called via LetsEncrypt.exe. A permission problem? Wrong powershell.exe?
That part of your question:
I've googled, some mentioning something about 32bit vs 64bit powershell
is already half of an answer. Some commands do not run properly if bitness of PowerShell process does not match bitness of operation system. So, you need to run powershell.exe, which located in this %windir%\System32\WindowsPowerShell\v1.0\ directory. But there is a little problem described in this documentation topic:
In most cases, whenever a 32-bit application attempts to access %windir%\System32, the access is redirected to %windir%\SysWOW64.
Thus, if 32-bit program on 64-bit OS invoke %windir%\System32\WindowsPowerShell\v1.0\powershell.exe, it will actually invoke 32-bit version of PowerShell from here %windir%\SysWOW64\WindowsPowerShell\v1.0\ instead of 64-bit one. To actually invoke 64-bit PowerShell from 32-bit application you need to use this trick:
32-bit applications can access the native system directory by substituting %windir%\Sysnative for %windir%\System32. WOW64 recognizes Sysnative as a special alias used to indicate that the file system should not redirect the access.
I've got the same error when running the following cmdlet:
PS> Remove-WebAppPool -Name 'Test'
Remove-WebAppPool : Cannot retrieve the dynamic parameters for the cmdlet. Retrieving the COM class factory for
component with CLSID {688EEEE5-6A7E-422F-B2E1-6AF00DC944A6} failed due to the following error: 80040154 Class not
registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).
At line:1 char:1
+ Remove-WebAppPool -Name 'Test'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Remove-WebAppPool], ParameterBindingException
+ FullyQualifiedErrorId : GetDynamicParametersException,Microsoft.IIs.PowerShell.Provider.RemoveAppPoolCommand
The reason was because I ran it using Windows PowerShell (x86) on my Windows 10 x64 machine.
When I tried the same but using Windows PowerShell, which is 64 bit version, it worked just fine.
I think your $guid is the issue. The GUID needs to be the GUID of the program to bind the cert to. For your example port 443 is only bound to a random GUID, and not your program's GUID. IIS and other apps have a static GUID that you will want to use. If the GUID for a powershell script then Get-host is the powershell host executing code so that's the GUID you need. It changes for every powershell session and the netsh binding needs to as well.
$appid = "appid={"+(get-host).InstanceId.guid+"}"
$certhash = ls Cert:\LocalMachine\my | where {$.EnhancedKeyUsageList -Match 'Server' -and $.subject -match (hostname)}|sort-object $_.NotAfter|select -expand Thumbprint -last 1
$cmdline='netsh http add sslcert ipport=0.0.0.0:443 certhash=' + $certhash + ' "' + $appid + '"'
netsh http delete sslcert ipport=0.0.0.0:443
Invoke-Expression $cmdline
A google search for "Cannot retrieve the dynamic parameters for the cmdlet" brought me here but my issue was using powershell from the command line, and the answer was to escape the double quotes on the command...
I've got a problem with the same error. This happens when i'm trying to Add-WebBinding to my IIS site remotely, using Invoke-Command from different agent machines at time.
It's worked for me, maybe it helps someone too:
$Mutex = New-Object -TypeName System.Threading.Mutex($false, "Global\Mutex")
if ($Mutex.WaitOne(300000)) {
#For example
#$Command = {
#New-WebBinding -name $iis_site_name -Protocol https -HostHeader
#$iis_host_name -Port 443 -SslFlags 1
#}
#Invoke-Command -Command $Command
} else {
Write-Warning "Timed out acquiring mutex!"
}
$Mutex.Dispose()

PowerShell: Invoke-Parallel throwing error with "start-process"

I'm using the Invoke-Parallel CmdLet that can be found at the link here. When I have some dummy code in my script block, it works correctly, but when I add a start-process... command it fails with an error saying
Get-RunspaceData : This command cannot be run due to the error: The system
cannot find the file specified.
At c:\crm\Interfaces\Batches\Invoke-Parallel.ps1:592 char:13
My script block looks like so. Long story short, I'm feeding file names into a block, and I am telling a 3rd party application to use that file as an input for a process. When the start-process line is removed, it works, with it, it fails.
$childFiles| Invoke-Parallel -ImportVariables {
$importformat = '-f ' + '"CU Imp P19 FI"'
$importfile = '-d ' + $_ + " " + $user + " " + $pass
Write-Host $importformat + " " + $_
start-process .\mmim.exe -ArgumentList $user, $pass, '-l:1',
$importformat, $importfile -Wait -NoNewWindow
return "blah"
}
Does anyone have any idea of what might be going on? My PowerShell version is the following
Major:5
Minor:0
Build: 10586
Revision: 117
Any help is greatly appreciated.
Each PowerShell runspace have its own current location. If Invoke-Parallel does not change current location of spawned runspaces to match current location of main runspace, then relative path .\mmim.exe may be resolved to entire different executable or not resolved at all and produce given error.

Resources