Archive for the ‘Uncategorized’ Category

Running WPF/XAML Code in Pow

Posted: April 13, 2017 in Uncategorized

I know I had planned to do the second part of the drain script, but I will come back to that.  (I am currently redesigning it)..  As part of one of my current projects, I am building a Windows Presentation Foundation (WPF) based GUI tool.  To that end, I started with Bo Prox’s excellent introductory articles on this subject.

In order to build the GUI, Bo recommends using Visual Studio Community Edition (excellent choice, since the other free options all seem to be “write the code, see the result” as opposed to WYSIWYG).  As part of the XAML code that gets created, he recommends that you remove 2 attributes from the default element, since it causes issues.  (I have forgotten to do this a couple of times on accident, and your PS script will absolutely throw terminating errors that make *no* sense..)

Now, as I went through multiple rounds of design changes, adjustments, etc., I became frustrated with having to constantly having to make copies (copy & paste) and then removing the attributes.  So, like any other scripter, I put together a small script to handle this automatically and save myself a *lot* of hassle.

The first part of the function is very straightforward:

function Run-XAMLCode
{
[cmdletbinding()]
param (
[parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateScript( { Test-Path -Path $_ } )]
[String]$XAMLPath
)

I’ve gone through detailed explanations on the start of advanced functions before, but for a quick overview:

  1. Declare the function name
  2. Assign [cmdletbinding()] which gives you the Common parameters (-verbose, -whatif, etc.)
  3. Create a new parameter that is required, and accepts input from the pipeline (not a critical item, but I typically just do this on every parameter)
  4. The ValidateScript line will throw an error if the supplied path can’t be found (just a nice piece of validation error that will kill the script if the path is mistyped accidentally)
  5. Declare the variable itself as a string ($XAMLPath)

The next 3 lines are important pieces, and in the process of creating some other scripts I learned something important..

Add-Type -AssemblyName PresentationFramework -ErrorAction SilentlyContinue
Add-Type -AssemblyName PresentationCore -ErrorAction SilentlyContinue
Add-Type -AssemblyName WindowsBase -ErrorAction SilentlyContinue

These 3 lines load the assemblies needed to run WPF forms. Without these, you will very likely have errors trying to run a WPF form. The point that I learned was that you don’t need to use the [System.Reflection.Assembly]::LoadWithPartialName(”) construction, you can simply use Add-Type to load them, and that is not being deprecated 🙂

The next two lines are very straightforward also.. create an XML object, and load the XAML file.

$XAML = New-Object -TypeName XML
$XAML.Load($XAMLPath)

And these lines are the critical step — removing the problematic attributes. Looking at the XAML code, the names feel unusual since they have the colons in the name. The only thing to be cautious about is if you change the main Window name in the form.. by default it is ‘Window’ which is what the script assumes. If you manually change that name, you will need to adjust these lines..

$XAML.Window.RemoveAttribute('mc:Ignorable')
$XAML.Window.RemoveAttribute('x:Class')

And the rest of the code is Bo’s code.

$xamlreader = New-Object -TypeName System.Xml.XmlNodeReader($XAML)
$Window = [windows.markup.XAMLReader]::Load($xamlreader)

$xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach-Object {
    New-Variable -Name $_.Name -Value $Window.FindName($_.Name) -Force
}
$Null = $Window.ShowDialog()
  1. Create an XMLNodeReader
  2. Load the XAML file using a XAMLReader
  3. These next lines are a practical usability function to create new variables for each of the controls in the WPF form.  He uses an XPATH query to locate each of the nodes, and create a variable with that same name that points to that node. It’s not strictly necessary, but this easily allows this script to be used as a template for a full blown WPF powershell application, as opposed to just seeing the form (which is what this function is for)
  4. Run the ShowDialog() to show the form.

And as long as the form is up on the screen, the powershell prompt used to run it will wait for the form to exit.

So, in summation, this function will let you run the form repeatedly to show the design without having to manually modify it, and without having to wait for Visual Studio to compile the form into an executable. Simply dot source it in your PS prompt, and you’ll the function ready to go.

Full Code listing:

function Run-XAMLCode
{
	<#
	.SYNOPSIS
		Allows the user to run a XAML form created by Visual Studio.
	
	.DESCRIPTION
		This lets the user run the XAML form repeatedly and quickly without having to manually 'fix' the XAML file each time.  
		The primary purpose of this is to be able to quickly make changes in the form, and then check the "look and feel" without having to modify it, or wait for VS to compile it.
	
	.PARAMETER XAMLPath
		This is the path to the XAML file.  It needs to either be the full path to the file, or it must be in the working directory for the script. 
	
	.EXAMPLE
		PS C:\> Run-XAMLCode -XAMLPath "$env:userprofile\Documents\VisualStudio\MyProject\MyProject.xaml"
	
	.NOTES
		This function is based on Bo Prox' article on creating WPF forms.
		The output is just the running WPF form, not an addressable XMLDocument
	
	.INPUTS
		system.string
	
	.OUTPUTS
		system.xml.xmldocument
	
	.LINK
		https://mcpmag.com/articles/2016/04/28/building-ui-using-powershell.aspx
		https://mcpmag.com/Articles/2016/05/05/WPF-and-PowerShell-Part-2.aspx?Page=2
	#>
	[cmdletbinding()]
   	param (
	    [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateScript( { Test-Path -Path $_ } )]
	    [String]$XAMLPath
    )

    Add-Type -AssemblyName PresentationFramework -ErrorAction SilentlyContinue
    Add-Type -AssemblyName PresentationCore -ErrorAction SilentlyContinue
    Add-Type -AssemblyName WindowsBase -ErrorAction SilentlyContinue

    $XAML = New-Object -TypeName XML
    $XAML.Load($XAMLPath)
    $XAML.Window.RemoveAttribute('mc:Ignorable')
    $XAML.Window.RemoveAttribute('x:Class')

    $xamlreader = New-Object -TypeName System.Xml.XmlNodeReader($XAML)
    $Window = [windows.markup.XAMLReader]::Load($xamlreader)

    Write-Verbose -Message "Create variables for each control, based off of the name"
    $xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach-Object {
        New-Variable -Name $_.Name -Value $Window.FindName($_.Name) -Force
    }

    $Null = $Window.ShowDialog()
}

I hope you find it as useful as I have..

David F.
(updated to add the help to the code)
 

In my job, I am on a team that manages hundreds of Citrix servers, and as any one who manages a large farm knows, there is a lot of work to be done, and automation is critical.

We manage XenApp 6.5 farms, and I came up with this script to drain servers a lot easier to manage, especially when used with a large number of farms.  The intent is to run this from a central management point, from an elevated powershell prompt in the security context of one of the Farm Administrators with local admin rights on each of the servers.

function Run-SystemDrain
{
   cmdletbinding()]
   param()

   change.exe logon /drain</pre>

This is a very straightforward opening to the script. I use the [cmdletbinding()] and the param() block to enable the -verbose parameter.  This was primarily for troubleshooting and diagnostics, but this could easily be left out.  The change.exe logon /drain puts the system in drain mode, and under XenApp 6.5, Citrix will detect this change and communicate it to the farm, and raise the load level of that server to 10000.  That process typically only takes a few seconds.

The key to this script is the do/until loop, and a couple of WMI calls.  I used WMI since this script needed to be Powershell 2.0 compatible.  The loop opens and gets the current sessions on the server, gets the disconnected sessions, and the count of the disconnected sessions.  If there are disconnected sessions, those sessions are logged off.

do
   {
      $TotalSessions = (Get-WmiObject -class Win32_TerminalService -Property TotalSessions).TotalSessions
      $DiscSessions = Get-WmiObject -Namespace Root/Citrix -Class MetaFrame_Session -Filter 'SessionState = 4'
      $DiscCount = (DiscSessions | Measure-Object).Count

      if ($DiscCount -gt 0)

The loop starts and uses WMI to get the total session count.  This is a straight Microsoft call, and has an easy property TotalSessions that does show all the sessions.  This does include the listeners (ICA-TCP, RDP-TCP, etc.).  If your servers are configured to have more than these 2 listers, you’ll need adjust the until line.

Unfortunately, the Win32_TerminalService object really does not contain the individual sessions with the necessary capabilities.  The key capability that we need is the ability to get the SessionID and to log that session off.  I dug through the object, and could not find an easy way to get that information from this object.  The other Win32* objects didn’t have this information either.

The Citrix MetaFrame_Session object does contain the SessionID and very importantly, a Logoff method.  These 2 items made this a natural selection.  In an RDP situation, you’d need to rely on the builtin qwinsta/quser commands and parse their output to accomplish this.  Also note that MetaFrame_Session exists under the Root/Citrix namespace, and not the default Root/CIMV2. The SessionState property of the MetaFrame_Session object shows the current state of the session.  The complete list of states is:

  • 0 = userLoggedOn (Active)
  • 1 = connectedToClient (Connected but not active)
  • 2 = connectingToClient (starting to connect, but not connected yet)
  • 3 = shadowingOtherSession (Currently shadowing another session)
  • 4 = loggedOnButNoClient (Disconnected)\
  • 5 = waitingForConnection()
  • 6 = listeningForConnection(Listeners only)
  • 7 = resetInProgress (being reset)
  • 8 = downDueToError (Down)
  • 9 = initializing ()

Another interesting note was that the $DiscSessions object with all the disconnected sessions didn’t seem to have a .Count property like virtually every other object I’ve worked with.   With that, I had to turn to the Measure-Object command to get that count.  Alternatively, I could have gone with:

$DiscCount = DiscSessions | Measure-Object | Select-Object -PropertyCount

The Select-Object cmdlet is slower than directly accessing the property though the (). mechanics, although I haven’t measured it. From a day to day perspective, the difference is very minimal, so I stuck with my “usual” preferred method.

This is the key loop section

{
   foreach ($Session in $DiscSessions)
   {
      $Session.Logoff()
   }
}

As we go through each $Session in the $DiscSessions collection, and use the Logoff() method to gracefully log out the session.

And then I follow it up with a start-sleep statement and the loop repeats until all of the sessions other than the listeners are gone.

    start-sleep -seconds 30
  }
  until ($TotalSessions -le 2)

And as I mentioned above, if you have more listeners than the default 2, you have to adjust this number to match.

So, the total script is:

function Run-SystemDrain
{
   cmdletbinding()]
   param()

   change.exe logon /drain
   do
   {
	  $TotalSessions = (Get-WmiObject -class Win32_TerminalService -Property TotalSessions).TotalSessions
	  $DiscSessions = Get-WmiObject -Namespace Root/Citrix -Class MetaFrame_Session -Filter 'SessionState = 4'
	  $DiscCount = (DiscSessions | Measure-Object).Count

	  if ($DiscCount -gt 0)
	  {
		  foreach ($Session in $DiscSessions)
		  {
			 $Session.Logoff()
		  }
	  }
		  start-sleep -seconds 30
	}
	until ($TotalSessions -le 2)
}

In the next post, I’ll cover using this script in practice..

David F.

I just got the Anniversary Update for Windows 10, and it took me a while to track down all the steps to actually enable this.. I had to hit several web pages, and some guesses, so I wanted to put a slightly more concise set of instructions on how to do this.

  1. Go into Add/Remove Programs in the Control Panel
    1. This can be opened with a quick command at the command line – appwiz.cpl
      appwiz
  2. Select Turn Windows features on or off
    TurnOnWindowsFeatures
  3. Select Windows Subsystem for Linux (beta)
    EnableLinux
  4. The installation will take a little time, and will probably require a reboot.
  5. After the reboot, log back in and click the Start button
  6. Type in “developer” and select the For developer settings
    DeveloperSettings
  7. Select Developer mode
    SelectDeveloperMode
  8. You can optionally select some other settings and click Apply in each section
    1. [Windows Explorer] Change policy to show Run as different user in Start
    2. [Remote Desktop] Change settings to allow remote connections to this computer
  9. Open up the command prompt, right click on the top left icon and select Properties
    CMDProperties
  10. Remove the checkbox for Use legacy console (requires relaunch)
    DisableLegacyMode
  11. Click OK, close the command prompt and relaunch it.
  12. Type in bash and hit enter.  You will be prompted to install the subsystem (it will take a few minutes.  I’ll update the screenshot for this later.

At this point, you’ll have bash available, and the system drive will be mounted under /mnt/c with full access to your filesystem.

David F.

I don’t normally post this kind of stuff.. but this was a little too good to pass up.

Microsoft is offering a huge list of free eBooks in various formats (pdf, mobi, docx, zip, ppt, ePub, xps) and the Microsoft Deployment Toolkits in both x86 & x64 formats.  A good number of them come in multiple formats so expect to have a lot of duplicates..

You can read about it here:
https://blogs.msdn.microsoft.com/mssmallbiz/2016/07/10/free-thats-right-im-giving-away-millions-of-free-microsoft-ebooks-again-including-windows-10-office-365-office-2016-power-bi-azure-windows-8-1-office-2013-sharepoint-2016-sha/

The list of links to get each item is here, although I’m not sure which one is which. I’ve made a few attempts to try and script something that would tell me which one was which (none off them worked) and I did manage to sort of write a script that would open up IE to each of the links sequentially for download purposes.  My goal was to try and control the number of instances using powershell jobs, but that mostly failed. I got them to open up in batches, but they just kept opening & opening & opening etc..  I was able to click through and save everything after the massive numbers of IE windows opened.

List of Links
http://ligman.me/29ngkYn http://ligman.me/29jL5wW http://ligman.me/29afIRV
http://ligman.me/29ngkYn http://ligman.me/29pyHgR http://ligman.me/29dmbfC
http://ligman.me/29ollRF http://ligman.me/29pyHgR http://ligman.me/29gvv67
http://ligman.me/29gvv67 http://ligman.me/29pzkHg http://ligman.me/29CWQ20
http://ligman.me/29CWQ20 http://ligman.me/1G0Cm7T http://ligman.me/1G0Cm7T
http://ligman.me/29dmbfC http://ligman.me/29ollRF http://ligman.me/29vDVrw
http://ligman.me/29CW4SV http://ligman.me/29CW4SV http://ligman.me/29G1z1N
http://ligman.me/29yWAAO http://ligman.me/29DHWHq http://ligman.me/29DHWHq
http://ligman.me/1HGwMgm http://ligman.me/1HGwMgm http://ligman.me/29yUVeH
http://ligman.me/29yUVeH http://ligman.me/29ErxWs http://ligman.me/29ErxWs
http://ligman.me/29EsHkr http://ligman.me/29EsHkr http://ligman.me/29v5zCF
http://ligman.me/29v5zCF http://ligman.me/1H32nUT http://ligman.me/1H32nUT
http://ligman.me/29yixk9 http://ligman.me/29yixk9 http://ligman.me/29vq6d2
http://ligman.me/29vq6d2 http://ligman.me/29cvrjG http://ligman.me/29pzjTR
http://ligman.me/29rPOf6 http://ligman.me/29cvrjG http://ligman.me/29ctW9z
http://ligman.me/29gvoHt http://ligman.me/29gvfEd http://ligman.me/29ctW9z
http://ligman.me/29LvcBg http://ligman.me/29DS3gJ http://ligman.me/1q9L65I
http://ligman.me/1q9L65I http://ligman.me/29pufLO http://ligman.me/29pufLO
http://ligman.me/29wODdA http://ligman.me/29wODdA http://ligman.me/29vch9q
http://ligman.me/29vch9q http://ligman.me/29EmfZc http://ligman.me/29EmfZc
http://ligman.me/29LXmMt http://ligman.me/29LXmMt http://ligman.me/29qwKfQ
http://ligman.me/29qwKfQ http://ligman.me/29sp3nZ http://ligman.me/29sp3nZ
http://ligman.me/29yWFFf http://ligman.me/29yWFFf http://ligman.me/29ZNaMN
http://ligman.me/29ZNaMN http://ligman.me/29gYy5h http://ligman.me/29gYy5h
http://ligman.me/29NhudA http://ligman.me/29jDRJf http://ligman.me/29conIf
http://ligman.me/29cnLlL http://ligman.me/29jDRJf http://ligman.me/29fNYRE
http://ligman.me/29cq9cw http://ligman.me/29acqhj http://ligman.me/29fJJCS
http://ligman.me/29nbxWK http://ligman.me/29fO9MV http://ligman.me/29fJJCS
http://ligman.me/29pr2x1 http://ligman.me/29pr2x1 http://ligman.me/29oa0kH
http://ligman.me/29i6ntm http://ligman.me/29n7JVF http://ligman.me/29fQsQ4
http://ligman.me/29fQo2J http://ligman.me/29idz8T http://ligman.me/29fQsQ4
http://ligman.me/29dfwlu http://ligman.me/29fLliP http://ligman.me/29i83TI
http://ligman.me/29diypX http://ligman.me/29fNpHO http://ligman.me/29oefNf
http://ligman.me/29diypX http://ligman.me/29jGTgC http://ligman.me/29crPhL
http://ligman.me/29FUJIk http://ligman.me/29jGTgC http://ligman.me/1HGwMgm
http://ligman.me/1HGwMgm http://ligman.me/1G2oDNG http://ligman.me/1G2oDNG
http://ligman.me/29u5uMT http://ligman.me/29u5uMT http://ligman.me/29rlRt3
http://ligman.me/29rlRt3 http://ligman.me/29Yd9V0 http://ligman.me/29Yd9V0
http://ligman.me/29s4EQ1 http://ligman.me/29s4EQ1 http://ligman.me/29vkv6E
http://ligman.me/29vkv6E http://ligman.me/29A3q8Z http://ligman.me/29A3q8Z
http://ligman.me/29H7ovO http://ligman.me/29H7ovO http://ligman.me/1dHMOui
http://ligman.me/1dHMOui http://ligman.me/1G2pw8X http://ligman.me/1G2pw8X
http://ligman.me/29gnaPK http://ligman.me/29d7GZH http://ligman.me/29i2Mvf
http://ligman.me/29rbpkY http://ligman.me/29rbpkY http://ligman.me/29e6cTO
http://ligman.me/29e6cTO http://ligman.me/29hJU1c http://ligman.me/29bcJtd
http://ligman.me/29bcJtd http://ligman.me/29bcRZO http://ligman.me/29d5mS2
http://ligman.me/29d5mS2 http://ligman.me/29cz6OD http://ligman.me/29fDult
http://ligman.me/29fDult http://ligman.me/29pEdjN http://ligman.me/29rwoY0
http://ligman.me/29rwoY0 http://ligman.me/29fTQrx http://ligman.me/29gfrkN
http://ligman.me/29gfrkN http://ligman.me/29rwoHw http://ligman.me/29rwoHw
http://ligman.me/29d5AbX http://ligman.me/29d5AbX http://ligman.me/29fudHc
http://ligman.me/29fudHc http://ligman.me/29FzGG5 http://ligman.me/29FzGG5
http://ligman.me/29osNwd http://ligman.me/29a22WR http://ligman.me/29a22WR
http://ligman.me/29alDpK http://ligman.me/29fDylj http://ligman.me/29fDylj
http://ligman.me/29jPTCx http://ligman.me/29d5XTw http://ligman.me/29d5XTw
http://ligman.me/29rUIci http://ligman.me/29hVdVa http://ligman.me/29hVdVa
http://ligman.me/29ijDhm http://ligman.me/29pn5e2 http://ligman.me/29pn5e2
http://ligman.me/29ckVx7 http://ligman.me/29ckVx7 http://ligman.me/29cfePX
http://ligman.me/29cfePX http://ligman.me/29vGIvY http://ligman.me/29vGIvY
http://ligman.me/29hfnPR http://ligman.me/29hfnPR http://ligman.me/29fCVFe
http://ligman.me/29fCVFe http://ligman.me/29a6YLu http://ligman.me/29a6YLu
http://ligman.me/29n4GwJ http://ligman.me/29n4GwJ http://ligman.me/29jAS3v
http://ligman.me/29jAS3v http://ligman.me/29ci56U http://ligman.me/29ci56U
http://ligman.me/29nW9L3 http://ligman.me/29nW9L3 http://ligman.me/29hV6ZM
http://ligman.me/29hV6ZM http://ligman.me/29d1qAV http://ligman.me/29d1qAV
http://ligman.me/29pfpbI http://ligman.me/29pfpbI http://ligman.me/29v8nwX
http://ligman.me/29v8nwX http://ligman.me/29uq452 http://ligman.me/29uq452
http://ligman.me/29d1wc3 http://ligman.me/29d1wc3 http://ligman.me/29Y1O7a
http://ligman.me/29Y1O7a http://ligman.me/29a7wRA http://ligman.me/29pnEEG
http://ligman.me/1FYtDD8 http://ligman.me/1FYtDD8 http://ligman.me/1HByNKS
http://ligman.me/1HByNKS http://ligman.me/1NCfcKC http://ligman.me/1NCfcKC
http://ligman.me/1HCDxl9 http://ligman.me/1HCDxl9 http://ligman.me/1HCCCRP
http://ligman.me/1HCCCRP http://ligman.me/1H4Q0e5 http://ligman.me/1H4Q0e5
http://ligman.me/1JI6V77 http://ligman.me/1JI6V77 http://ligman.me/1CSMobd
http://ligman.me/1CSMobd http://ligman.me/1jWMJA2 http://ligman.me/1jWMJA2
http://ligman.me/1m6xucg http://ligman.me/1m6xucg http://ligman.me/1onTg9n
http://ligman.me/1onTg9n http://ligman.me/1n49kzj http://ligman.me/1n49kzj
http://ligman.me/1sgBtn4 http://ligman.me/1sgBtn4 http://ligman.me/1qZlnOJ
http://ligman.me/1qZlnOJ http://ligman.me/TWa2Dg http://ligman.me/TWa2Dg
http://ligman.me/1vM9mwt http://ligman.me/1vM9mwt http://ligman.me/1qzON6Q
http://ligman.me/1qzON6Q http://ligman.me/1rB8nl1 http://ligman.me/1rB8nl1
http://ligman.me/TL3pn1 http://ligman.me/TL3pn1 http://ligman.me/1vM9H2d
http://ligman.me/1vM9H2d http://ligman.me/29odbJ6 http://ligman.me/1LSKTC0
http://ligman.me/1LSKTC0 http://ligman.me/1qC1pu4 http://ligman.me/1qC1pu4
http://ligman.me/1dHSpRh http://ligman.me/1dHSpRh http://ligman.me/1LO5k1Y
http://ligman.me/1LO5k1Y http://ligman.me/1M7Xr5v http://ligman.me/1M7Xr5v
http://ligman.me/29jLNtX http://ligman.me/29jLNtX http://ligman.me/29agpuw
http://ligman.me/29cv8FE http://ligman.me/29cv8FE http://ligman.me/29ieXIo
http://ligman.me/29dmCXi http://ligman.me/29jLjnM http://ligman.me/29jLjnM
http://ligman.me/29agkqx http://ligman.me/29gvTBJ http://ligman.me/29pztuz
http://ligman.me/29pztuz http://ligman.me/29dmTJT http://ligman.me/29ieSEI
http://ligman.me/29ieSEI http://ligman.me/29hB9CQ http://ligman.me/29fOWdV
http://ligman.me/1JPNIAt http://ligman.me/1JPNIAt http://ligman.me/29Xnqk8
http://ligman.me/29Xnqk8 http://ligman.me/29oRWlp http://ligman.me/29csX4C
http://ligman.me/29jH8bx http://ligman.me/29pcQFA http://ligman.me/29pcQFA
http://ligman.me/29FkNr3 http://ligman.me/29FkNr3 http://ligman.me/12FIapt
http://ligman.me/12FIapt http://ligman.me/13WvGXa http://ligman.me/1bPPb6C
http://ligman.me/12FIZ1I http://ligman.me/12FIZ1I http://ligman.me/16CaDM1
http://ligman.me/19LwMLI http://ligman.me/1JHmqiB http://ligman.me/1JHmqiB
http://ligman.me/1KHqGNK http://ligman.me/1KHqGNK http://ligman.me/1M7Ycve
http://ligman.me/1M7Ycve http://ligman.me/1M7Ycve http://ligman.me/1LSOsIu
http://ligman.me/1LSOsIu http://ligman.me/1UrQDFx http://ligman.me/1UrQDFx
http://ligman.me/TUmyTW http://ligman.me/TUmyTW http://ligman.me/1NLviCk
http://ligman.me/1NLviCk http://ligman.me/17iaq4l http://ligman.me/17iaq4l
http://ligman.me/1bPRqqz http://ligman.me/17iah0Q http://ligman.me/1287Jt4
http://ligman.me/29djdrk http://ligman.me/29rLH2H http://ligman.me/29fNPxT
http://ligman.me/29djdrk http://ligman.me/29ddm60 http://ligman.me/29gsnHt
http://ligman.me/29nc2QS http://ligman.me/29fOrnd http://ligman.me/29dk0Ze
http://ligman.me/29aewh2 http://ligman.me/29fOrnd http://ligman.me/29gteI8
http://ligman.me/29gteI8 http://ligman.me/29ibJVq http://ligman.me/29fKMD6
http://ligman.me/29cthAz http://ligman.me/29cthAz http://ligman.me/29ohqUT
http://ligman.me/29crGiw http://ligman.me/29gsFOl http://ligman.me/29ncgrb
http://ligman.me/29fKWdi http://ligman.me/29gsFOl http://ligman.me/29djvi9
http://ligman.me/29FW41O http://ligman.me/29ddKBp http://ligman.me/29djvi9
http://ligman.me/29dkhf4 http://ligman.me/29dkhf4 http://ligman.me/29rML6Q
http://ligman.me/29hxyEN http://ligman.me/29fPweO http://ligman.me/29fPMug
http://ligman.me/29fPnIe http://ligman.me/29fPweO http://ligman.me/29hxogT
http://ligman.me/29hxogT http://ligman.me/29deg2k http://ligman.me/29pwCBO
http://ligman.me/29fPAec http://ligman.me/29fPAec http://ligman.me/29deQgz
http://ligman.me/29afaLE http://ligman.me/29pxarl http://ligman.me/29pxarl
http://ligman.me/29ne5V5 http://ligman.me/29csBjh http://ligman.me/29dkZZK
http://ligman.me/29dkZZK http://ligman.me/29ojrAy http://ligman.me/29jJLdx
http://ligman.me/29df2fw http://ligman.me/29df2fw http://ligman.me/29cu6sX
http://ligman.me/29dlaE5 http://ligman.me/29FZx0B http://ligman.me/29FZx0B
http://ligman.me/29idnpO http://ligman.me/29dlH9d http://ligman.me/29cs157
http://ligman.me/29cs157 http://ligman.me/29dkq1V http://ligman.me/29aelm3
http://ligman.me/29ycEnb http://ligman.me/29ycEnb http://ligman.me/29w5gr9
http://ligman.me/29w5gr9 http://ligman.me/29pfQ50 http://ligman.me/29pfQ50
http://ligman.me/1sgMWDe http://ligman.me/1sgMWDe http://ligman.me/1sgMWDe
http://ligman.me/29w68w8 http://ligman.me/29w68w8 http://ligman.me/29viFmi
http://ligman.me/29viFmi http://ligman.me/29zYWzl http://ligman.me/29zYWzl
http://ligman.me/29H2bo1 http://ligman.me/29H2bo1 http://ligman.me/29AT60J
http://ligman.me/29AT60J http://ligman.me/29xtlNa http://ligman.me/29xtlNa
http://ligman.me/29xtHDs http://ligman.me/29xtHDs http://ligman.me/29GpFsY
http://ligman.me/29GpFsY http://ligman.me/29ddd2b http://ligman.me/29fJpEc
http://ligman.me/29cs7Fg http://ligman.me/29ddd2b http://ligman.me/29fJA2g
http://ligman.me/29fJA2g http://ligman.me/29fJA2g http://ligman.me/29cqzzq
http://ligman.me/29ddbYb http://ligman.me/29ddbYb http://ligman.me/29rLlZV
http://ligman.me/29ialCf http://ligman.me/29pvj5I http://ligman.me/29pvj5I
http://ligman.me/29hwj8y http://ligman.me/29dj7zY http://ligman.me/1sl39Hs
http://ligman.me/1sl39Hs http://ligman.me/1anyEJj http://ligman.me/1anyEJj
http://ligman.me/17icbPc http://ligman.me/ZZezok http://ligman.me/12S035G
http://ligman.me/12S035G http://ligman.me/12RZWY1 http://ligman.me/13PlvVY
http://ligman.me/12FMEMP http://ligman.me/12FMEMP http://ligman.me/128a1ID
http://ligman.me/19LCgpM http://ligman.me/13Pn1XY http://ligman.me/13Pn1XY
http://ligman.me/13WChkr http://ligman.me/12FN71F http://ligman.me/ZZh7Ts
http://ligman.me/ZZh7Ts http://ligman.me/14HcD5O http://ligman.me/17UHSNJ
http://ligman.me/19LEPIz http://ligman.me/19LEPIz http://ligman.me/11VIxdB
http://ligman.me/12FObmf http://ligman.me/11HXnjD http://ligman.me/11HXnjD
http://ligman.me/14fCxLS http://ligman.me/16CkUI4 http://ligman.me/17VqB79
http://ligman.me/17VqB79 http://ligman.me/13XRfqr http://ligman.me/19fCnqV
http://ligman.me/11WvSqL http://ligman.me/11WvSqL http://ligman.me/1002Upx
http://ligman.me/14HMJ1Q http://ligman.me/10ttQ3n http://ligman.me/10ttQ3n
http://ligman.me/15fRBI9 http://ligman.me/19fDuqE http://ligman.me/11IMDlh
http://ligman.me/11IMDlh http://ligman.me/10tu5eD http://ligman.me/128NAD6
http://ligman.me/1bRgXzV http://ligman.me/1bRgXzV http://ligman.me/14givRo
http://ligman.me/18VNatF http://ligman.me/128Ogso http://ligman.me/128Ogso
http://ligman.me/13Qbl7k http://ligman.me/10tuWvL http://ligman.me/16dFAWc
http://ligman.me/16dFAWc http://ligman.me/11lkViX http://ligman.me/1bRhVMn
http://ligman.me/14HOjki http://ligman.me/14HOjki http://ligman.me/1bRiXbc
http://ligman.me/13QcfRi http://ligman.me/16E4lf4 http://ligman.me/16E4lf4
http://ligman.me/1005lbB http://ligman.me/14HOxb2 http://ligman.me/16E6G9L
http://ligman.me/16E6G9L http://ligman.me/14gkBRc http://ligman.me/1006fEV
http://ligman.me/16dHXZ9 http://ligman.me/16dHXZ9 http://ligman.me/13QdHDb
http://ligman.me/11IQ8bd http://ligman.me/17jgPfG http://ligman.me/17jgPfG
http://ligman.me/15fXTHQ http://ligman.me/11vx5Cy http://ligman.me/N1JiHO
http://ligman.me/OudHlO http://ligman.me/OudJdr http://ligman.me/N1I2o4
http://ligman.me/N1I2o4 http://ligman.me/Oue0NG http://ligman.me/Oue2oE
http://ligman.me/N1HQW0 http://ligman.me/N1HQW0 http://ligman.me/Ouebsh
http://ligman.me/OuecfK http://ligman.me/N1Ienp http://ligman.me/N1Ienp
http://ligman.me/OuelQu http://ligman.me/OueoMd http://ligman.me/N1J8A8
http://ligman.me/N1J8A8 http://ligman.me/OueFPb http://ligman.me/OueIKU
http://ligman.me/N1I935 http://ligman.me/N1I935 http://ligman.me/OueUd4
http://ligman.me/OueVxy http://ligman.me/N1HMW7 http://ligman.me/N1HMW7
http://ligman.me/Ouf6sO http://ligman.me/Ouf9og http://ligman.me/N1Jo27
http://ligman.me/N1Jo27 http://ligman.me/OufgQN http://ligman.me/OufkQs
http://ligman.me/N1HEpM http://ligman.me/N1HEpM http://ligman.me/Oufwzg
http://ligman.me/OufCXN http://ligman.me/N1JfvI http://ligman.me/N1JfvI
http://ligman.me/OufRSs http://ligman.me/OufVlq http://ligman.me/N1HX3Q
http://ligman.me/N1HX3Q http://ligman.me/Oug5Jl http://ligman.me/Oug74a
http://ligman.me/1H1Exty http://ligman.me/1H1Exty http://ligman.me/1S1f34H
http://ligman.me/1S1f34H http://ligman.me/1HGqihD http://ligman.me/1HGqihD
http://ligman.me/1G2ccS5 http://ligman.me/1G2ccS5 http://ligman.me/1ffeiJo
http://ligman.me/1ffeiJo http://ligman.me/1NKjUqp http://ligman.me/1NKjUqp
http://ligman.me/1KEShAt http://ligman.me/1KEShAt http://ligman.me/1S1i4C0
http://ligman.me/1S1i4C0 http://ligman.me/1ReR3Qq http://ligman.me/1ReR3Qq
http://ligman.me/1dGxnSW http://ligman.me/1dGxnSW http://ligman.me/1IZCarE
http://ligman.me/1IZCarE http://ligman.me/1H2Bq3J http://ligman.me/1H2Bq3J
http://ligman.me/1Rf7BaZ http://ligman.me/1Rf7BaZ http://ligman.me/1LRIveQ
http://ligman.me/1LRIveQ http://ligman.me/1dGxEW7 http://ligman.me/1dGxEW7
http://ligman.me/1omCrM6 http://ligman.me/1omCrM6 http://ligman.me/1j5aDhH
http://ligman.me/1j5aDhH http://ligman.me/1n3mkVY http://ligman.me/1n3mkVY
http://ligman.me/1n3mAUZ http://ligman.me/1n3mAUZ http://ligman.me/1vKOGot
http://ligman.me/1vKOGot http://ligman.me/1H7bxTv http://ligman.me/1H7bxTv
http://ligman.me/1G0DEjb http://ligman.me/1G0DEjb http://ligman.me/1qC1pu4
http://ligman.me/29pbiLY http://ligman.me/29pbiLY http://ligman.me/29dlTVV
http://ligman.me/29rOYz9 http://ligman.me/29ie2rq http://ligman.me/29dlTVV
http://ligman.me/29fQN5c http://ligman.me/29idEct http://ligman.me/29nevuE
http://ligman.me/29fQN5c http://ligman.me/29olaWI http://ligman.me/29olaWI
http://ligman.me/29pz4Im http://ligman.me/29fQRlQ http://ligman.me/29hzPzU
http://ligman.me/29rON6X http://ligman.me/29cumbH http://ligman.me/29hzPzU
http://ligman.me/29cv1JW http://ligman.me/29dmh6L http://ligman.me/29dfUkt
http://ligman.me/29cv1JW http://ligman.me/1giniO7 http://ligman.me/1giniO7
http://ligman.me/29H7K5O http://ligman.me/29H7K5O http://ligman.me/1qbEeVc
http://ligman.me/1qbEeVc http://ligman.me/1ewwcq6 http://ligman.me/1ewwcq6
http://ligman.me/1H1MFKr http://ligman.me/1H1MFKr http://ligman.me/1pxniH4
http://ligman.me/1pxniH4 http://ligman.me/1dG2ZZ9 http://ligman.me/1dG2ZZ9
http://ligman.me/29v9igV http://ligman.me/29hofVk http://ligman.me/29v9igV
http://ligman.me/29idcee http://ligman.me/29idcee http://ligman.me/29rOvx0
http://ligman.me/29ctkki http://ligman.me/29ctkki http://ligman.me/29okalt
http://ligman.me/29hofVk http://ligman.me/29i2CEe http://ligman.me/29FT71n

I know it has been several months since my last post, but I’ve been steadily increasing my powershell knowledge, and creating scripts that I can’t make public.

Recently, I had a need to make some registry changes by script. I found tons of examples on how to do this, however, none of them really seemed to address what happens if the registry key is not there.  Eventually, I did find one that did, however, the way it was written, it only tolerated 1 level missing. So, if you needed to create a registry path several levels deep that didn’t exist, it did not provide a way to do this.

This script is going to be a reusable tool that I will put in my toolkit, so the first few lines are my “typical” advanced function lines.

function Set-DRFRemoteRegistry
{
	[CmdletBinding()]
	param
	(
		[Parameter(Position = 0, Mandatory = $false, ValueFromPipeline=True, ValueFromPipelinebyName=$true)]
		[Alias("CN")]
		[string[]]$ComputerName = $Env:COMPUTERNAME,

		[Parameter(Position = 1, Mandatory = $true)]
		[ValidateSet("ClassesRoot", "CurrentConfig", "CurrentUser", "DynData", "LocalMachine", "PerformanceData", "Users")]
		[string]$Hive = "LocalMachine",

		[Parameter(Position = 2, Mandatory = $true)]
		[string]$RegKeyPath,

		[Parameter(Position = 3, Mandatory = $true)]
		[string]$ValueName,

		[Parameter(Position = 4, Mandatory = $true)]
		$ValueData,

		[Parameter(Position = 5, Mandatory = $true)]
		[ValidateSet("String", "ExpandString", "Binary", "DWord", "MultiString", "QWord", "Default")]
		[string]$ValueType,

		[Parameter(Position = 6, Mandatory = $false)]
		[Switch]$Force
	)

Most of this is pretty self explanatory.  I’m defining the function, using an approved verb set, and the noun starts with my initials to keep it unique – drf, and a descriptive name – remoteregistry.  And then of course the open brace and then the [CmdletBinding()].  CmdLetBinding tells powershell that the function should act like part of the shell, and to support some of the common parameters (Debug,ErrorAction,ErrorVariable,OutVariable,OutBuffer,Verbose,WarningAction,WarningVariable).  http://msdn.microsoft.com/en-us/library/dd901844(v=vs.85).aspx  CmdletBinding has some parameters you can put inside the parenthesis, but I am not covering those here.

The next part is to setup my parameters.  Since I’m creating registry values.. I need several pieces of information

  • What Computer(s)?
  • What Registry Hive?
  • What Registry Path?
  • What Registry Value name?
  • What’s the data in the Value?
  • What’s the data type for that Value?
  • Do I need to build the entire path? (the real focus of this article)

Inside the Param( ) set, each parameter consists of a [Parameter()] tag with options inside the ( ) marks, a variable name for the parameter.

And here is the list of parameters inside of the Param( ) block

[Parameter(Position = 0, Mandatory = $false, ValueFromPipeline=True, ValueFromPipelinebyName=$true)]
[Alias("CN")]
[string[]]$ComputerName = $Env:COMPUTERNAME,

The first parameter is the name of the computer to apply the registry value to.  The first option is the Position parameter.  This tells Powershell that if the function is run and the parameter names are not specfied, then the first position (0) is going to be the ComputerName.  It’s typically bad form to not specify the parameter names, but in a pinch, this lets you do that.  Without it, the function will ignore that parameter.

The second option is Mandatory.  This option is pretty self-explanatory.  If a Mandatory = $true is specified, then the function will not run if that parameter is provided – it will prompt the user to provide a value if it is not provided.

The next two options ValueFromPipeline and ValueFromPipelineByPropertyName. allow the function to accept input from the pipeline.  They are different, and the difference is subtle.  I’d love to say I understand what the real difference is, but I don’t.

On the next line, I’m setting a .Net PropertyType accelerator – [string].  When you use this, then the variable immediately following it is forced to be a string type.  But, notice the interior brackets – [string[]].  These brackets tell powershell that it should expect to have multiple values for that particular parameter.  It’s also called an accelerator because it allows direct access to any of the .net properties of a string.

Next, we have another bracketed option – [Alias(“CN”)].  This line tells powershell that you have defined an alias to use inline for your named parameter.  So, instead of using the full variable name ($ComputerName), you can simply use -CN.  You can have mutliple aliases separated by commas inside the parenthesis.

Then we have the variable name ($ComputerName in this case), followed by an equals sign and a value.  This assignment (=) tells it that if the parameter is not supplied by the user, then this should be assumed to be the default. So, we are telling it that the $ComputerName should default to the local computername (by environment variable) if one is not supplied.

The next parameter is structured very similarly:

[Parameter(Position = 1, Mandatory = $true)]
[ValidateSet("ClassesRoot", "CurrentConfig", "CurrentUser", "DynData", "LocalMachine", "PerformanceData", "Users")]
[string]$Hive = "LocalMachine",

It is very similar to the first parameter, except we gave it the next position, and we use [ValidateSet()].

  • [ValidateSet()] This option allows you to force the parameter to only accept a value from your comma separated list.  It also gives the ability for powershell to know what the acceptable values are, so that when you type in the command, and enter the parameter, you can hit Tab and have the value filled in.  Powershell editors also use this to provide a form of intellisense.

We’ve pre-supplied the list of values that can be accepted.  These values are the names of the registry hives that are used by several .Net functions.  Most of the value names are pretty straight forward, but just in case:

  • “ClassesRoot” – this represents HKEY_CLASSES_ROOT
  • “CurrentConfig” – this represents HKEY_CURRENT_CONFIG
  • “CurrentUser” – this represents HKEY_CURRENT_USER
  • “DynData” – this represents HKEY_DYNAMIC_DATA
  • “LocalMachine” – this represents HKEY_LOCAL_MACHINE
  • “PerformanceData – this represents HKEY_PERFORMANCE_DATA
  • “Users” – this represents HKEY_USERS

There are plenty of other validation sets and options that can be used, but they are not part of this article.

Next is the parameter for the Registry key path. Since this is separate from the Hive, it is spelled out in a normal path fashion, such as Software\Microsoft\Windows. If the path has spaces in it, you’ll need to use quotes around the path. Such as ‘Software\Microsoft\Windows NT\CurrentVersion’. (And since it is powershell, we use single quotes when possible, since it tells powershell that there are no replaceable variables in the string. If there were going to be, we would use double quotes instead.

[Parameter(Position = 2, Mandatory = $true)]
[string]$RegKeyPath,

Next we have parameters 3 & 4

[Parameter(Position = 3, Mandatory = $true)]
[string]$ValueName,

[Parameter(Position = 4, Mandatory = $true)]
$ValueData,

This covers the name of the registry value to create and the data for the value. However, notice that the $ValueData parameter does not have a type accelerator. This is due to the fact that we don’t know exactly what kind of data we’ll be using, such as binary data, dwords, etc.

There is also another useful parameter type – [Switch]. This parameter either takes effect, or doesn’t depending on whether it exists.  In this case, I am using it to indicate force.  For this script, if the switch is present it will mean we forcibly create the full registry key path if it does not exist.

This is a lot of explanation just for the first part of the script, but, Part 2 will have the working body of the script.

Until then..

David Figueroa