Powershell: dealing with / in Registry property names - powershell

Given this (real) Registry path...
HKEY_CLASSES_ROOT\Local Settings\MrtCache\C:%5CProgram Files%5CWindowsApps%5C89006A2E.AutodeskSketchBook_1.6.0.0_x64__tf1gferkr813w%5Cresources.pri\1d3438f5876f755\6dfb7f2f\#{89006A2E.AutodeskSketchBook_1.6.0.0_x64__tf1gferkr813w?ms-resource://89006A2E.AutodeskSketchBook/Files/Assets/AppLogo/Orion_Tiny.png}
where the property name is...
#{Microsoft.Office.OneNote_17.8625.20901.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Office.OneNote/Files/images/OneNoteAppList.png}
I am trying to figure out how to properly deal with extracting the data value. I need to differentiate the path to the containing key from the property name, but because the property name contains / and Split-Path converts those to \ and treats them as key delimiters, I get bad data out of that Cmdlet. From a programming standpoint the solution is to not start with a single path. However, I am somewhat constrained by existing data in XML files that provides only a single path. For 99.9% of cases, including drive and UNC file & folder paths, registry Key & Property paths as well as URL paths, Split-Path works. But for this very specific situation it fails. Is there a .NET solution that can be depended on? Or is this a case where there is no solution other than to break up the data and curse Microsoft for their inconsistency and incomplete solutions?
I get that this example is probably a situation I will never actually run into, but I have been burned before with things like assuming anything with an extension is a file and then finding someone (usually Autodesk) has decided to name a bunch of folders with a . in the name, causing code with that assumption about naming conventions to fail. So I am looking for a consistent way to deal with this, if one exists. Ideally in PS 5.1, not PS Core, as I cannot and will not demand that all my users upgrade to a new version of PowerShell to address such an edge case.
EDIT: I should also mention that a similar issue arrises when there is a / in a key name, and I want to verify that the key exists. HKLM\Software\Key/Name is a perfectly valid path, and Split-Path will soil itself every time and come back with Name as the leaf, not Key/Name. Because Split-Path doesn't actually understand what's valid as a registry key name, it seems.

Related

PowerShell - How to programmatically determine which registry keys a Group Policy Object modifies?

As the title, how to automatically map GPOs to their corresponding Windows registry values in PowerShell?
I am now very experienced in PowerShell, but determine which registry value is modified by a GPO with PowerShell is still a little bit too hard to me, currently I am able to use Process Monitor to create custom filters to only include activities of mmc.exe gpedit.msc, then use Tools->"Registry Summary" to get registry entries modified by gpedit.msc, then I can export the entries to a .csv file and import it in powershell and do further commands, but this method doesn't show which registry key is modified by a GPO, so I have to manually change a GPO and switch to "Registry Summary" to see which registry entry is modified by the last GPO change, this is very inefficient; I am aware there are some existing group policy to registry mappings, but they are incomplete.
So how can I determine which registry entry is modified by a GPO in PowerShell? Any help is appreciated.
Update
As far as I know, Windows Group Policy definitions are *.admx files stored in C:\Windows\PolicyDefinitions folder, and the localization files that make the policy definitions be displayed in gpedit.msc are *.adml files with the same name of corresponding *.admx file stored in a subfolder named the language code of the locale (i.e. en-US) inside C:\Windows\PolicyDefinitions folder, all information about the group policies should be in them, so if I can parse these files, I can map GPOs to their equivalent registry keys.
Just opened one of the .admx files in Notepad++, and found out these files are encoded in plain text, completely human readable, they are just .xml files renamed, so it's easier than I thought...
I am trying to write a PowerShell script that does this mapping thing...
UPDATE1
So far I have achieved these:
[xml]$xml=get-content C:\Windows\PolicyDefinitions\AppXRuntime.admx
$xml.policydefinitions.policies.policy[0].name
$xml.policydefinitions.policies.policy[0].key
Of course when actually running the script I will use two loops, first loop foreach .admx in folder, second loop use for $index to assign values, I can do it right now.
Just foreach loop through filenames without extension, cast C:\Windows\PolicyDefinitions${filename}.admx to $xml1, and C:\Windows\PolicyDefinitions\en-US${filename}.adml to $xml2, use:
($xml2.policydefinitionresources.resources.stringtable.string | where {$_.id -eq $xml1.policydefinitions.policies.policy[$i].name})."#text"
To get display names, it is really simple. But the key does not contain the hivename (i.e. HKEY_LOCAL_MACHINE).
I have worked out this:
$xml.policydefinitions.categories.category.parentcategory.ref + $xml.policydefinitions.categories.category.name
To get path of GPO, I assume if it starts with Windows: then the key is stored in HKEY_LOCAL_MACHINE.

Can I package a CSV file as a module resource

I have a custom PowerShell module with a corresponding module manifest. In one command in my module I have a hard-coded array of hash tables. This was fine at first but occasionally I have to go back and add new hash tables to this hard-coded array and the array is becoming quite long. It is becoming difficult to manage this data in this way. What I would really like to do is move this collection out into an external resource (e.g. a CSV file) and have the command read the data from the CSV file. Actually, this is what I preferred from the beginning but it has only just now become painful enough that I feel compelled to figure out how to do this.
My question is how would I go about doing this? Or can it even be done? I have read quite a bit about module manifests but I do not ever recall reading anything that describes a way to specify additional resources in the manifest file or how to load those resources in such a way as to be 'private' to a module. I suppose I could just drop the CSV file in the module's folder with all the other PowerShell files and then maybe I can find it using $PSScriptRoot but that does not seem very 'official' (and I am not 100% sure it would work). Plus, by doing it that way there is nothing in the manifest that would suggest to somebody else that there are other resources that are required for the module to function properly.
Is there a best practice for something like this or am I coming at this all wrong?
The manifest definition does have a key for this; it is called FileList and is essentially an array of files. Since the description generated by the New-ModuleManifest cmdlet says, "List of all files packaged with this module," that is what I specified when I used it. (I didn't have to list the .psm1 file since it is listed elsewhere in the manifest.)
# List of all files packaged with this module
FileList = #(
'script1.ps1',
'script2.ps1',
'Microsoft.Web.Publishing.Tasks.Dll',
'transform.proj',
'some_file.xml'
)
As for locating the files, I simply use $PSScriptRoot, just like you suggested.
To my knowledge, there isn't anything that automatically handles installation of the module. It's still up to you to get it into a folder in the PSModulePath environment variable.

Start-BitsTransfer's Destination field is not mandatory

I had a bug in a script where I'd specified -Description $dest instead of -Destination $dest on a call to Start-BitsTransfer.
It didn't error / ran quickly for a small file and took a while for a large one.
As such I think the file was copied to my machine; I just can't find where it was copied to...
Question
Why isn't Destination a mandatory field?
Where do files go by default / when Destination isn't specified?
The snarky answer to the first part of your question would probably be "because Microsoft said so". Since I wasn't involved in the decision making I can't give you a definitive answer, but example 7 of the cmdlet documentation mentions that
The destination path cannot use wildcard characters. The destination path supports only a relative directory, a rooted path, or an implicit directory (the current directory).
So I would suspect that the parameter was made optional to allow transferring files "here" (to the current working directory) without having to explicitly specify a destination, i.e. for simplicity of use.

Run executable using wildcard path

I have an executable in a directory that is versioned, so the directory changes when the tool is updated.
The current command I run is the following:
.\packages\Chutzpah.4.1.0\tools\chutzpah.console.exe .\Tests\chutzpah.json
I want to do something like the following:
.\packages\Chutzpah**\tools\chutzpah.console.exe .\Tests\chutzpah.json
Windows command line doesn't like to expand wildcards but I'm hoping this is possible with powershell.
Simple answer here could be to use resolve-path which
Resolves the wildcard characters in a path, and displays the path contents.
So in practice you should be able to do something like this.
$path = Resolve-Path ".\packages\Chutzpah**\tools\chutzpah.console.exe" -Relative
& $path ".\Tests\chutzpah.json"
Note that Resolve-Path has the potential to match more that one thing.
Actually, Windows will expand the wild cards, but if the non-wildcard portion is not unique, you'll get the (I think) FIRST match.
So I think that really your problem is
"How do I tell which version I should be executing"
Which, if you have project files, etc., you might be able to extract from there.
In fact, you might well be wanting to extract something from the solution packages.config file, assuming that the .\packages\ prefix is there because you are wanting to run tests against files that are in a NuGet package.
Can you supply some more details?
EDIT:
OK, so you probably need to do something like System.IO.Directory.GetDirectories(path, pattern)
https://msdn.microsoft.com/en-us/library/6ff71z1w(v=vs.110).aspx
And depending on what you'd done with "Chutzpah" you'll get one or more matches that you could use to select the correct path.

Windows Installer Automation and Installshield Basic MSI: Mystery String during Chained MSI

EDIT: Turns out the mystery string was a simple MD5 hash of the name of the file (including the extension and capitalization).
I'm attempting to automate the process of creating a Chained MSI through InstallShield. In the GUI, this involves going to Releases, adding a chained package, linking to the MSI and streaming the file into the project.
I've reverse engineered what exactly happens behind the scenes by analyzing the project file as XML. It essentially just comes down to table edits. I understand you can use Windows Installer Automation to open an *.ism file and access the database tables (LINK).
Yet, there is a single field in the ISChainPackageData table which I cannot seem to generate or figure out how it was calculated. It is the column titled, File. It is a 32 character hex string preceded by an underscore. I have discovered that the only attribute that determines this field is the name of the MSI file being streamed. For example:
Linking to a chained MSI by the name of Test.msi, yields _29B31F67F21C9EE77CBF8C4C5D24ACE9.
Changing the name would change this. Changing the file, including replacing it with an empty file of the same name, does not.
I believe it is some kind of simple hash of the name, but I haven't had any luck guessing it.
Does anyone have any insight on what they might be using here?
Thanks!
Close. It's a hash-based GUID of a combination of a few things. I'd have to trudge up the code to find out exactly what, but it's at least the relative path and filename, and possibly something related to the package in question (probably its primary key value).
This is used to generate a unique key for each file you include with a package, without allowing duplicate files. (Windows Installer doesn't like backslashes in its primary keys.) The actual value here isn't meaningful; if you're careful to avoid duplicate keys and don't overlap file path and name combinations, you can probably put in any valid key value you like. However that may prevent the IDE from detecting duplicates itself.