Powershell latest MSBuild path - powershell

After trying a number of approaches, including what seemed an excellent suggestion at http://www.bdevuyst.com/powershell-path-msbuild-exe/, which gave me an error, I tried to break it down. Though I get the latest MSBuild registry key (14.0), I still get this error when I try to extract the path to MSBuild:
Get-ItemProperty : Cannot find path 'C:\USERS\user\Desktop\HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\MSBuild\ToolsVersions\14.0' because it does not exist.
At C:\USERS\mtroi\Desktop\VSS_POC1_Setup.ps1:529 char:5
+ Get-ItemProperty -Path $MsBuildVersion -Name MSBuildToolsPath
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\USERS\mtroi\...lsVersions\14.0:String) [Get-ItemProperty], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetItemPropertyCommand
# 32bit or 64bit local OS?
if($ENV:PROCESSOR_ARCHITECTURE -eq "x86")
{$HKLMpath = "HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\"}
elseif($ENV:PROCESSOR_ARCHITECTURE -eq "AMD64")
{$HKLMpath = "HKLM:\SOFTWARE\Wow6432Node\Microsoft\MSBuild\ToolsVersions\"}
else
{Write-Host "Local processor architecture not supported. Exiting installation..."; EXIT}
# Which path (version) of local MSBuild?
$_decSep = [System.Threading.Thread]::CurrentThread.CurrentUICulture.NumberFormat.CurrencyDecimalSeparator;
$MsBuild1 = #(Get-ChildItem -Path $HKLMpath | Where { $_.Name -match '\\\d+.\d+$' })
ForEach ($build in $MsBuild1)
{
$Expression=$Expression+","+[System.Convert]::ToDecimal($build.Name.Substring($build.Name.LastIndexOf("\") + 1))
}
$MsBuildVersion = Get-ChildItem -Path $HKLMpath | Where { $_.Name -match '\\\d+.\d+$' } |
Sort-Object -Property #{Expression=$Expression} -Descending |
Select-Object -First 1 #| Get-ItemProperty -Name MSBuildToolsPath
Get-ItemProperty -Path $MsBuildVersion -Name MSBuildToolsPath

Related

One-liner to find a registry key and delete parent works but returns an error

I am using a one liner to find a registry key based on a value then delete it's parent. The one liner produces an error but if I separate the search result into a variable and run remove-item there is no error. I'd like to find out what causes this and if I should I be worried?
Produces an error:
Get-ChildItem -path HKLM:\SOFTWARE\Classes\Installer\Products -Recurse -ErrorAction Stop | Get-ItemProperty -Name "PackageName" -ErrorAction SilentlyContinue| where { $_.PackageName -cmatch "BigFixAgent\.msi" } | Select-Object -ExpandProperty PSParentPath | Remove-Item -Recurse -confirm
Get-ChildItem : The registry key at the specified path does not exist.
At line:1 char:1
+ Get-ChildItem -path HKLM:\SOFTWARE\Classes\Installer\Products -Recurs ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (HKEY_LOCAL_MACH...4D34\SourceList:String) [Get-ChildItem],
ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException,Microsoft.PowerShell.Commands.GetChildItemCommand
Produces No Errors:
$offending_key= Get-ChildItem -path HKLM:\SOFTWARE\Classes\Installer\Products -Recurse -ErrorAction Stop | Get-ItemProperty -Name "PackageName" -ErrorAction SilentlyContinue| where { $_.PackageName -cmatch "BigFixAgent\.msi" } | Select-Object -ExpandProperty PSParentPath
Remove-Item -Recurse $offending_key -confirm
That's because it is iterating recursively through the registry tree, and you deleted part of that tree in the middle of the iteration. You could put parenthesis around everything but the last part, so that it finishes iteration first, or you could put the last part in a ForEach-Object loop, and add a break after it so once that key is found it removes it and stops looking.
Complete Get-ChildItem iteration before deleting:
(Get-ChildItem -path HKLM:\SOFTWARE\Classes\Installer\Products -Recurse -ErrorAction Stop | Get-ItemProperty -Name "PackageName" -ErrorAction SilentlyContinue| where { $_.PackageName -cmatch "BigFixAgent\.msi" } | Select-Object -ExpandProperty PSParentPath) | Remove-Item -Recurse -confirm
Stop after finding the first matching key:
Get-ChildItem -path HKLM:\SOFTWARE\Classes\Installer\Products -Recurse -ErrorAction Stop | Get-ItemProperty -Name "PackageName" -ErrorAction SilentlyContinue| where { $_.PackageName -cmatch "BigFixAgent\.msi" } | Select-Object -ExpandProperty PSParentPath | ForEach-Object {$_ | Remove-Item -Recurse -confirm; break}

PowerShell logon script to log Microsoft Office version

I came across PowerShell script to log Microsoft Office version of remote computer on domain. I want to run it as logon script to I modified OpenRemoteBaseKey to OpenBaseKey and this is the code:
$version = 0
$reg = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine', 'Default')
$reg.OpenSubKey('software\Microsoft\Office').GetSubKeyNames() |% {
if ($_ -match '(\d+)\.') {
if ([int]$matches[1] -gt $version) {
$version = $matches[1]
}
}
}
if ($version) {
Add-Content -Path \\server\share\oversion.txt -Value "$env:computername $env:username : $version"
}
else {
Add-Content -Path \\server\share\oversion.txt -Value "$env:computername $env:username : 0"
}
but now I receive error:
You cannot call a method on a null-valued expression.
At line:4 char:1
+ $reg.OpenSubKey('software\Microsoft\Office').GetSubKeyNames() |% {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
but not sure what this means since GetSubKeyNames seems valid: https://learn.microsoft.com/en-us/dotnet/api/microsoft.win32.registrykey.getsubkeynames?view=netframework-4.7.2, also it works with OpenRemoteBaseKey, can someone point me to right direction, please?
I'm not sure why OpenRemoteBaseKey works, but OpenBaseKey doesn't because I cannot reproduce that..
You might however try the more Powershell way of doing this:
$version = 0
Get-ChildItem -Path 'HKLM:\SOFTWARE\Microsoft\Office' -Name | Where-Object {$_ -match '(\d+)\.\d+'} | ForEach-Object {
$version = [math]::Max([int]$_, $version)
}
Add-Content -Path \\server\share\oversion.txt -Value "$env:COMPUTERNAME $env:USERNAME : $version"

Find Available Drive Letter and Change

I am trying to create a PowerShell script that will find an available drive letter, map a network drive, and then change to that mapped drive. I found the following which mapped \\server\share as the D: drive:
$Drive = New-PSDrive -Name $(for($j=67;gdr($d=[char]$J++)2>0){}$d) -PSProvider FileSystem -Root \\server\share\
I can manually enter D:, but how can I change this in a script? I was thinking along the lines of this:
$Drive = $Drive.Trim(":")
But the statement above throws the following error:
Method invocation failed because [System.Management.Automation.PSDriveInfo] does
not contain a method named 'Trim'.
At line:1 char:1
+ $Drive = $Drive.Trim(":")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
You could check a list of potential drive letters against the list of currently assigned drive letters and use the first unused one:
$used = Get-PSDrive | Select-Object -Expand Name |
Where-Object { $_.Length -eq 1 }
$drive = 90..65 | ForEach-Object { [string][char]$_ } |
Where-Object { $used -notcontains $_ } |
Select-Object -First 1
New-PSDrive -Name $drive -PSProvider FileSystem -Root \\server\share
Set-Location "${drive}:"
or a random one from that list:
$used = Get-PSDrive | Select-Object -Expand Name |
Where-Object { $_.Length -eq 1 }
$unused = 90..65 | ForEach-Object { [string][char]$_ } |
Where-Object { $used -notcontains $_ }
$drive = $unused[(Get-Random -Minimum 0 -Maximum $unused.Count)]
New-PSDrive -Name $drive -PSProvider FileSystem -Root \\server\share
Set-Location "${drive}:"
The functions used in Ansgar's answer are likely returning the DVD/CD drive because it has no media in it. To get a free drive letter, that is likely* not the CD/DVD drive we search from i-z:
$dl=ls function:[i-z]: -n | ?{ !(test-path $_) } |select -last 1

Powershell ErrorAction not silent

I've got a PS script that looks for the Office15 folder on computers on our network. For the most part, the script works as intended. In fact, this is just me being picky. I set -ErrorAction SilentlyContinue, but the error messages when the Office15 folder is not found still appear on screen. I'm wondering if I'm doing something wrong or just don't really understand what my script is doing.
$filePath = "\\"+$computer+"\c$\Program Files (x86)\Microsoft Office\"
$listing = Get-ChildItem $filePath | where-object { $_.name -eq "Office15" } | Select-Object Name -ErrorAction SilentlyContinue
With this script as-is, I get errors like the following:
Get-ChildItem : Cannot find path '\\COMPNAME\c$\Program Files (x86)\Microsoft Office\' because it does not exist.
At C:\Users\someGuy\bootTime\checkOffice.ps1:16 char:20
+ $listing = Get-ChildItem $filePath | where-object { $_.name -eq "Office1 ...
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (\\COMPNAME\c$\Pr...crosoft Office\:String) [Get-ChildItem], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
I pump all of the valid results into a text file, so the other parts of the script work just fine, and I get the expected results otherwise. I'm only really interested in learning what I might be doing wrong here.
You need to pass the error action to gci:
$listing = Get-ChildItem $filePath -ErrorAction SilentlyContinue | where-object { $_.name -eq "Office15" } | Select-Object Name

Powershell - Export Multiple CSV's into unique folders

I have been working on a PowerShell script for the better part of well a week or two. I've been able to get some parts of it working however I'm unable to fully get this automated.
I deal with a lot of CSV files on a daily basis, I have been tasked with uploading them into our software however sometimes they're too large to handle so I break them down based upon their "type" (it's a column in the CSV) and I export it to a single CSV per "type". I've been able to accomplish this with the following:
$file = gci -Filter "*.csv";
Import-Csv $file `
| Group-Object –Property “type” `
| Foreach-Object `
{
$path=$_.name+”.csv” ; $_.group `
| Export-Csv –Path $path –NoTypeInformation
}
So this works wonderfully, for each individual CSV. Unfortunately I don't have the time to do this for each individual CSV. Now I come to my other PowerShell script:
get-childitem -Filter "*.csv" `
| select-object basename `
| foreach-object{ $path=$_.basename+".csv" #iterate through files.
if(!(Test-Path -path $_.basename)) #If the folder of the file can't be found then it will attempt to create it.
{
New-Item $_.basename -type directory; $file=$_.basename+".csv";
Import-Csv $file `
| Group-Object -Property "Type" `
| Foreach-Object {
$path=$_.name+".csv"; $_.group `
| `
if(!(Test-Path -path $path2))
{
New-Item $path2 -type directory
Export-Csv -Path $path2 + "\" + $path -NoTypeInformation
}
else
{
"Failed on: " + $_.basename
#Export-Csv -Path $_.basename + "\" + $path -NoTypeInformation
}
}
}
else
{
Import-Csv $path `
| Group-Object -Property "Type" `
| Foreach-Object {$path=$_.basename+".csv" ; $_.group
if(Test-Path -path $._)
{
New-Item $path2 -type directory
Export-Csv -Path $path2 + "\" + $path -NoTypeInformation
}
#else
#{
Write-Host "Failed on: $_.basename"
#Export-Csv -Path $_.basename + "\" + $path -NoTypeInformation
#}
}
}
}
I just can't wrap my head around "why" this isn't working effectively. I have two conditionals. Is there a folder for the CSV? If no create one. I have to have another one because one of the "types" contains a \ which errors out if I don't have the folder, so I automatically try to create it. When I run the script I get the Path is null.
The Error is:
The term ' ' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or
if a path was included, verify that the path is correct and try again.
At C:\Users\c.burkinshaw\foldermake.ps1:11 char:26
+ | ` <<<<
+ CategoryInfo : ObjectNotFound: ( :String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Test-Path : Cannot bind argument to parameter 'Path' because it is null.
At C:\Users\c.burkinshaw\foldermake.ps1:12 char:45
+ if(!(Test-Path -path <<<< $path2))
+ CategoryInfo : InvalidData: (:) [Test-Path], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.TestPathCommand
Any help would be greatly appreciated, if you have questions please don't hesitate to ask.
You have not defined $path2 anywhere, so something like test-path -path $path2 will say path is null. And in one place you are using $._ which will again give errors.
Edit after question updated with error message:
Your error message also says the same
Test-Path : Cannot bind argument to parameter 'Path' because it is
null. At C:\Users\c.burkinshaw\foldermake.ps1:12 char:45
+ if(!(Test-Path -path <<<< $path2))
Also the other error is in:
$path=$_.name+".csv"; $_.group `
| `
what are you trying to do here with the $_.group?
It is not proper. You cannot do $_.group | and provide some if statement.
Other comments:
Why are using $_.basename and then appending .csv? You could have just used $_.name. Try to not use the select-object basename - I don't see the value.
Extract the common import-csv and export-csv part into a function.