SpecialFolders with multiple versions of PowerShell - powershell

Is there a means in PS2.0 of getting at some of the Special Folders that are not enumerated until later versions?
For example, [Environment]::GetFolderPath("ProgramFilesX86") works in PS5.0 but produces an enumeration error in PS2.0. I was thinking maybe there is a .NET based approach, but my Google fu has me thinking that even in .NET prior to Framework 4 there is no way, but that also seems like a pretty glaring oversight.

The first of the below commands returns 47 in PowerShell 5.1.1 on Windows 10 1803. In PowerShell 2.0 on the same system, it returns 23. If you removed the pipe to Measure-Object and the Count property dotted notation, it'll actually show the names of the folders you can use. That's the seconds of the the below commands.
([enum]::GetNames([System.Environment+SpecialFolder]) | Measure-Object).Count
([enum]::GetNames([System.Environment+SpecialFolder]) | Sort-Object) -join "`n"
PowerShell 2.0 Special Folders:
ApplicationData
CommonApplicationData
CommonProgramFiles
Cookies
Desktop
DesktopDirectory
Favorites
History
InternetCache
LocalApplicationData
MyComputer
MyDocuments
MyMusic
MyPictures
Personal
ProgramFiles
Programs
Recent
SendTo
StartMenu
Startup
System
Templates

Related

Get installed software product context using powershell

I can easily get all installed software products on a machine using
Get-WmiObject -Class Win32_Product
Now I'd like to also fetch the Product Context. How can I access this information for every installed product using PowerShell.
In VB I did that by using the WindowsInstaller COM-Object and then querying the information. In essence this:
Set Com = CreateObject('WindowsInstaller.Installer')
Set Products = Com.ProductsEx(vbNullString,"S-1-1-0",7)
For Each P in Products
context = P.Context
Which I dont not manage to replicate in PowerShell
I realize this question is a bit stale, but I disagree with what seems to be the prevailing notion that working with Windows Installer in PowerShell is somehow a "pain" and more complicated than working with it in VBScript (this post is just one of many).
I have found that VBScript Windows Installer code translates quite literally to PowerShell, which means there are numerous examples of VBScript Windows Installer scripts that can be adapted to PowerShell and used to learn how to work with Windows Installer in PowerShell.
For this specific question of install context, the PowerShell code is quite similar to the VB code the OP gave.
# code must be run with admin rights to use "S-1-1-0" SID
enum InstallContext {
FirstVisible = 0 # product visible to the current user
None = 0 # Invalid context for a product
UserManaged = 1 # user managed install context
UserUnmanaged = 2 # user non-managed context
Machine = 4 # per-machine context
All = 7 # All contexts. OR of all valid values
AllUserManaged = 8 # all user-managed contexts
}
$Installer = New-Object -ComObject WindowsInstaller.Installer
foreach ($P in $Installer.ProductsEx("", "S-1-1-0", 7)) {
[InstallContext]$P.Context()
}
NOTE: I used Enums (about Enum - PowerShell | Microsoft Docs) with PowerShell here since tagMSIINSTALLCONTEXT is an enum in the msi.h file.
It's a pain to use that com object in powershell. I would use vbscript instead and save the text output to a powershell variable, or find an msi powershell module. That com object doesn't have a "type library" or support "IDispatch". The Windows Powershell in Action appendix for 2nd edition goes into it, but even there it's not pretty. That vbscript code has errors.

How to get OS Build number in Windows 10 1809 ISO using Powershell

We will get the OS Build number every time we build an OS. However I cant find where is the OS Build number inside the windows ISO file. I tried to search from the install.wim but cannot find it. I might miss something. Please let me know where can i get that number.
Example of OS Build number : 18362.239
So what you have there is [OS build number].[Updated Build Revision (UBR) number]. You can get UBR by querying registry. That is the only way i know of.
(Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name UBR).UBR
To get the OS build, you can use the WMI class or registry method.
(get-wmiobject -Class win32_OperatingSystem).BuildNumber
or
(Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name CurrentBuild).CurrentBuild
or
[system.environment]::osversion.version.build
Or from the sysinfo which would be messy.
Now its just a matter of marrying the 2.
Why not use DISM?
dism /Get-WimInfo /WimFile:X:\sources\install.wim
Then if you look at the created date (e.g. If the created date is: 3/19/2017 then the build number is the "1703" = Year & Month of Windows 10 release) or look at the build number
An ISO file typically comes with several Windows images.
To get the build of, say, the first image of an ISO mounted on D: (images list is 1-indexed):
(get-windowsimage -imagepath "D:\sources\install.wim" -index 1).build

How to change extended windows file attributes via Powershell without using attrib.exe?

This seems to be a quite simple question, yet googling gave me nothing.
Here is the error (PS 5.1, win 10.0.14393 x64):
Set-ItemProperty $myFileInfo -Name Attributes -Value ([System.IO.FileAttributes]::Temporary)
The attribute cannot be set because attributes are not supported. Only the following attributes can be set: Archive, Hidden, Normal, ReadOnly, or System.
attrib.exe seems to support most of System.IO.FileAttributes. Unfortunately is does not seem to work with files referenced using FileSystem PSDrives. That is what I am using extensively.
Making wrapper for SetFileAttributes kernel API call would be the last resort.
Am I missing any other [more simple] ways setting these extended file attributes?
PS. Apart from [System.IO.FileAttributes]::Temporary I am interested in setting [System.IO.FileAttributes]::NotContentIndexed.
You can edit the Attributes property of a [FileInfo] object directly. For example, if you wanted to exclude all files in the C:\Temp folder from being content indexed, you could do this:
Get-ChildItem C:\Temp | ForEach{
$_.Attributes = $_.Attributes + [System.IO.FileAttributes]::NotContentIndexed
}
That would get each file, and then add the [System.IO.FileAttributes]::NotContentIndexed attribute to the existing attributes. You could probably filter the files to make sure that the attribute doesn't already exist before trying to add it, since that may throw errors (I don't know, I didn't try).
Edit: As noted by #grunge this does not work in Windows Server 2012 R2. Instead what you have to do is reference the value__ property, which is the bitwise flag value, and add the bitwise flag for NotContentIndexed. This should work for you on any Windows OS:
Get-ChildItem C:\Temp | ForEach{
$_.Attributes = [System.IO.FileAttributes]($_.Attributes.value__ + 8192)
}

Is the PowerShell ConsoleShell on .NET 4.0 approved for production?

After reading several other blog posts and articles (references found below) there appear to be several ways to run PowerShell on .NET 4.0 but few are sufficient for our purposes. Due to how we deploy our software we cannot update the registry or change add an application. This leaves us with two options, create our own shell by using ConsoleShell or override PSHost. We would like to be able to use the first option, ConsoleShell, due to it's simplicity but would like to know what issues we may encounter and whether doing so is recommended.
Reference
Based on other questions I have seen that you can use the following methods to run PowerShell as .NET 4.0. None of these methods appear to be officially sanction by Microsoft but the first shown below is included as a work around in this Microsoft connect issue.
The options to run PowerShell in .NET 4.0 appear to include:
Update the app.config to include .NET 4.0 as supported
Add a registry setting to switch the version
Use ConsoleShell to host your own PowerShell Console
Implement your own PowerShell host, PSHost , as done by PoshConsole or Nuget
As far as it is not officially approved (to my knowledge), then nobody can approve it but you. If your scenario works and passes reasonable tests, approve and use it.
I use PowerShell on .NET 4.0 but with the option 4, and used to use the option 1, too (I do not like the option 2, personally). Thus, I approved it, production or not, I use it a lot and it works. I still use PowerShell on .NET 2.0 for two reasons: 1) PowerShell.exe starts faster (especially x64); 2) to be sure that part of my PowerShell development is compatible with .NET 2.
Another thought. If something does not work properly in PowerShell on .NET 2.0 (there are some issues, indeed, see Connect) then the fact "it is approved for production" itself does not help much. One has to overcome existing issues, .NET 2 or .NET 4 does not matter.
P.S. I should have mentioned that I tried the option 3 as well. I did not find use cases suitable for using it in my scenarios. But I did not find any issues in using ConsoleShell either.
P.P.S Yet another option. Make a copy of PowerShell.exe, rename it into MyConsoleShell.exe, use it together with MyConsoleShell.exe.config configured for .NET 4. As far as you are going to use a separate application anyway, then why not to consider this?
I'm a bit of a powershell N00b, but I threw this together as a way of forcing an arbitrary script to use .NET 4.0 in my script:
# Place this at the top of your script, and it will run as a .NET 4 ps script.
# #############################################################################
if ($PSVersionTable.CLRVersion.Major -lt 4) { try {
$cfgPath = $Env:TEMP | Join-Path -ChildPath ([Guid]::NewGuid())
mkdir $cfgPath | Out-Null
"<configuration><startup useLegacyV2RuntimeActivationPolicy='true'><supportedRuntime version='v4.0'/></startup></configuration>" | Set-Content -Path $cfgPath\powershell.exe.activation_config -Encoding UTF8
$darkMagic = 'COMPLUS_ApplicationMigrationRuntimeActivationConfigPath'
$old = [Environment]::GetEnvironmentVariable($darkMagic)
[Environment]::SetEnvironmentVariable($darkMagic, $cfgPath)
& powershell.exe $MyInvocation.MyCommand.Definition $args
} finally {
[Environment]::SetEnvironmentVariable($darkMagic, $old)
$cfgPath | Remove-Item -Recurse
return
}}
# ##############################################################################
# My script starts here:
echo "Your arguments are: $args "
echo "The CLR Major Version is : $($PSVersionTable.CLRVersion.Major)"
It places a check in the beginning of the script, and if it's not .NET 4.0 it creates a configuration file, sets an environment variable and re-runs the powershell script so that it runs under .NET 4.0.
it does incur a bit of a startup time penalty of about a second or so on my pc, but at least it works :)

How to find new cmdlets in Powershell v3.0

I wanted to find the new cmdlets / functions in Powershell. I used the following approach, but not sure if it is comprehensive / correct. Any ideas to find this in a better / different way?
Run the below once from v2 and once from v3 ( and write to a different file)
get-command -Module Microsoft.PowerShell.* |
select -expand name | out-file e:\poshv2.txt
Then use Compare-Object to see what's added ( or removed)
Compare-Object (gc e:\poshv2.txt) (gc e:\poshv3.txt)
My observation based on this is that there were 25 new cmdlets added ( and none were removed)
One question that was raised as a comment on my blog was that Disable-PsRemoting, which appeared in this list, is not really new. The reason it appeared was that it was not in the modules under Microsoft.Powershell.* ( and it was not a cmdlet), but it is in v3.0.
The only difference which you already noted is that in v2 Disable-PsRemoting was a function and in v3 it's a cmdlet. I wrote about cmdlet and parameter changes in v3 (using a similar compare method) on the PowerShell Magazine website.
http://www.powershellmagazine.com/2011/09/15/how-to-find-out-whats-new-in-powershell-vnext/