6 July 2016

Occasionally I get installers from vendors that just don’t play nice. OK… maybe it’s more than occasionally… Often it’s in the form of not having any parameters to do a nice, silent install and other times it’s one little quirky thing. Today that one thing was the uninstall of an MSI that forced a reboot despite using the /norestart parameter. My sripted install can handle reinstalling the app but the removal part kept forcing a reboot so the re-install never happened.

Super annoying and I don’t know why packages do forceful things like this. You really need to let the admin doing the software push decide these things. In my case, skipping the reboot causes no harm. After trying to strategically place  Shutdown -a  in my script (and failing to work) I got the idea that, if I initiate my own shutdown then the installer (or uninstaller) can’t force one of their own! So I tried it…

And it turned out I was right. Before I kicked off the uninstaller, which was an MSI, so in the form of msiexec.exe /X{GUID} , I used   shutdown -r -t 240  to schedule a reboot and give the uninstaller 4 minutes to run. The length of time can be anything as long as it’s enough time to get all of the tasks done because we’ll be cancelling it with   shutdown -a  once all the tasks are done.

Because Windows will only allow there to be one restart (pending or immediate) any shutdown command the package uses will be ignored and told there is already a pending system reboot.

The one drawback is that the scheduled shutdown is not silent.  In my company we use AutoIt so all I did was throw in a line to WinWait for the pending reboot notice to show up and then send an [Enter] to it.  There is still an icon in the tray and a balloon pops up when you cancel the reboot but that is fine with me.  I’m going to do a system restart at the end of my script anyway.

Hope this helps!


1 May 2016

I always love when I’m asked at work to do the maximum imaginable result with the least amount of resources. It’s like being asked to move a mountain and handed a single rope. While not impossible (you could do it in many trips with the rope tied to chunks), it does make you wonder, while you’re laboring, how on Earth you got yourself into this.

Thankfully I was ready for a challenge. Recent projects were the same ol’, same ol’, and I was bored. When I was asked to take the reigns on vetting an app virtualization product to use I was pretty stoked. I spent a few weeks on one product that has a great Cloud capability but we eventually settled on Microsoft’s App-V for various reasons. I was then asked to figure out how to publish apps without the need of a management server and database. 😐

Management doesn’t want the overhead of maintaining those two additional servers…. OK. I took it as a challenge and set to figuring it out. One goal I made for myself was to figure out a way to do it in Powershell that was generic. We use PVS with out Citrix environment but the thought of having to open up an image every time we add or change an App-V package wasn’t very appealing. After all, the goal is the least amount of maintenance and hassle. Mind you, integration for App-V packages is now built in with newer versions of XenApp but we aren’t there yet. So, the initial foray was quite easy and successful. To publish an individual app it only takes 3 lines of Powershell code. However, that’s a specific application and I wanted to go generic. That’s where the fun began.

My inital plan was to write a script that lived in the PVS image and would accept parameters. I’d use the published app in the XenApp console to pass those parameters depending on what app was published. My two parameters were the name of the App-V package and the path to the EXE that needed to be launched. The script was capable of taking the name of the package and building the DFS path to where the packages are stored. The folder names are the same as the package names for ease of use. The script would then add the package to the local disk and then publish it globally. The path to the EXE is, well, to launch the correct app. This didn’t work out so well because the EXE’s for a package are burried deep in the folder structure for App-V and the Windows command line can only accept a command that is 255 characters long or less.

I then dug into the cmdlets for App-V looking for a way to identify executables based off a package. Nothing in the way of EXE’s. I then tried creating shortcuts in a folder at the root of C inside the package’s XML configuration. That failed and it appears an App-V package wont write a shortcut in most directions. It has to be either the public desktop, start menu, or inside the packages folder structure. I figured if I can’t do it by manually editing the XML file, then perhaps I can do it by adding it in the package itself. Nope. It writes the code to the XML file to create the shortcut just like I had manually, but it never actually creates it. I’m assuming this probably isn’t a bug, since virtualized apps are supposed to be contained and writing shortcuts anywhere on the computer would be a mess. It’s probably just an undocumented feature.

After a little bit of feeling frustrated I got the sudden inspiration to return to the XML file. Why? Because the XML file lists every single EXE that was packaged! I didn’t need to pass an entire path because I could just read it from the XML file. So that’s what I did. My second parameter in the Citrix published app was now just the EXE name I needed to launch. The publishing script in the PVS image now, after adding and publishing the package, will read in the XML configuration file and search it for the EXE name being passed to it. Once it finds it, it takes the path for it in the XML, does a little string modification in the way of parsing, replacing, and concatenating, and voila, I have the entire path to the program I need to launch.

It took me about a day and a half to finish the entire script. I added a couple more things like popping up an error message if it encountered a problem adding and publishing the App-V package. Also, a loading screen that cleverly waits for the app to launch and show up before disappearing so the end-user doesn’t think nothing is happening. Script is below. I also published it as my first script to poshcode.org.

The location for the Citrix published app looks something like this: powershell.exe -file C:\Scripts\AppVAddAndPublish.ps1 “AppName” “Exename.exe”
(don’t forget to set-executionpolicy in the PVS image or override it here)

###################################################################
##  Description:  Generic script for publishing and launching App-V
##  packages in Citrix published app is calls the script and passes
##  it the name of the package and the name of the EXE to launch.  
##  App-V packages should be saved in a DFS structure that uses the
##  name of the app as the folder.
##
##  This script is provided as is and may be freely used and 
##  distributed so long as proper credit is maintained.
##
##  Written by: thegeek@thecuriousgeek.org
##  Website:  http://thecuriousgeek.org
##  Date Modified:  05-01-2016
###################################################################


param(
	[string]$appName,
	[string]$exeName
)

# Window message to the user so they don't think nothing is happening
Write-Host "`n`n                        Your application is loading..."

Import-Module AppvClient
# Try and get package to see if it's loaded
$package = Get-AppVClientPackage -Name $appName

# If the package isn't already loaded, load it
If ( !$package ) {
	Try {
		# Add and publish package.  Make sure to update with your own path
		$package = Add-AppVClientPackage \\PathToApp-VPackageStorage\$appName\$appName.appv
		Publish-AppVClientPackage $appName -Global | Out-Null
		$didComplete = $true
	} Catch {
		# If it didn't complete, popup an error message to the user with details
		$errorMessage = "Error Item: $_.Exception.ItemName `n`nError Message: $_.Exception.Message"
		$didComplete = $false
		$wshell = New-Object -ComObject Wscript.Shell
		$wshell.Popup($errorMessage,0,"Error loading application",0x0)
	}
} else {
	# Package is already loaded
	$didComplete = $true
}

# In order to know when the app has launched, we'll check the number of window titles
# This is the count before we try to start the app
$windowTitleCount = (get-process | where {$_.MainWindowTitle}).Count

If ($didComplete) { 
	# Get the local package directory as set in App-V Config, used for building path to EXE
	# Powershell can't expand regular environment variables so have to use CMD to echo it back
	$cmdExpand = "cmd /c echo $( (Get-AppVClientConfiguration | where {$_.Name -eq 'PackageInstallationRoot'}).Value )"
	$packageRoot = Invoke-Expression $cmdExpand
	$packagePath = "$packageRoot\$($package.PackageId)\$($package.VersionId)\Root\VFS"
	# Pull in deployment config because it contains the path to all executables.  Make sure to update with your own path
	[XML]$packageConfig = Get-Content "PathToApp-VPackageStorage\$appName\$($appName)_DeploymentConfig.xml"
	$packageApps = $packageConfig.DeploymentConfiguration.UserConfiguration.Applications.Application.Id
	# Loop through each app/exe listed in the config and find the one that contains the $appExe passed to the script
	ForEach ($app in $packageApps) {
		If ($app -match $exeName) {
			$appPath = $app
			break
		}
	}
	# Get rid of the special characters App-V uses to reference variables in it's application path so we can create the real path
	$appPath = $appPath -replace "(\[|\{|\}|\])",""  #previous regex  [^0-9a-zA-Z\\\s\.]
	$fullPath = "$packagePath\$appPath"
	Start $fullPath
	
	# Check number of window titles again and keep looping until the count is greater than the starting one we took earlier
	$currentTitleCount = (get-process | where {$_.MainWindowTitle}).Count
	While ($windowTitleCount -ge $currentTitleCount) {
		$currentTitleCount = (get-process | where {$_.MainWindowTitle}).Count
	}
}

Of course, now we’re upgrading to 7.8 for Citrix so all of this work is probably going to be useless! It was fun and challenging anyway 🙂


3 April 2016

A coworker pinged me the other day needing help with a short script he was running on a C# web page. He was pulling worker group information from Citrix and wanted to sort them based off of the number of servers in each worker group. There isn’t a property on the object returned from Get-XAWorkergroup that includes a server count. He said he googled it and couldn’t find anything online about sorting by the count of a property on an object. I thought he was crazy (both in wanting to sort by server count and that he couldn’t find anything) so I gave it a quick search myself. Surprisingly, there isn’t anything.

I thought for a moment how I would go about doing this and first looked into measuring and object and seeing if I could pipe that to be sorted. Doesn’t work. After wishing “if only there was a property for server count” I remembered the Add-Member cmdlet and the ability to add properties to an object. I love this feature of Powershell and how easy it is to do. It’s more complicated in the programming environment because you’d need to create a new class or extend the existing one. It’s so easy and flexible to do it on the fly when you need to.

I first started by trying to pipe the array of my objects to Add-Member but that failed. Add-Member doesn’t appear to be “smart enough” to recognize you are piping an array of objects to it and so it acts on the array object itself. It wasn’t until after 10 minutes of frustration and realizing this that I figured out why my calculating server count kept coming back as 0. The array itself doesn’t have a property “ServerNames” so it couldn’t give me a count! Powershell can be confusing if you’re not paying attention in that Get-Member (GM) tells you the members of the objects in the array, it doesn’t give information about what the variable is. Always use the GetType() method to determine what exactly your variable is. So, after modifying it slightly I did a ForEach on the array, and inside the ForEach I used Add-Member to get an accurate server count and then add it as a property to each object. After that, it’s just a matter of piping it to sort. Here is the final code, one line:

add-pssnapin citrix.xenapp.commands; get-xaworkergroup -comp socxa65pddc01 | foreach { add-member -InputObject $_ -NotePropertyName ServerCount -NotePropertyValue $_.ServerNames.count -Passthru } | sort -property ServerCount -descending

The one thing to keep in mind is that Add-Member does not output anything by default so you have to use -PassThru so that you have something to pipe to sort!


12 January 2016

I couldn’t figure out why a mousedown event was firing 3 times while writing some jQuery code. I googled it and most of the answers that attempted to explain it were saying something to the effect of “an event is getting bound to an element multiple times”… somehow. That seemed odd to me as my example code was short and sweet and I didn’t see how I could be accidentally triggering the same event on the same element multiple times. I tried using .off() and .unbind() to clear it but it wasn’t working. Obviously there was another reason for my particular issue and once I reminded myself the code was executing the way it was supposed to and the problem was me, I could look at it differently. It turns out my root cause is very different from some others.

One of the goals I’m trying to achieve for responsive interface is that when a user clicks on an element, that element comes to the forefront. This interface will have multiple dives and they will be draggable and resizable. Given the limited real estate, I want the user to be able to enlarge an element to see more data and I want to make sure that despite it’s position in the code, it can be on top of everything. Because I wanted it to work for anything they clicked on, I used * to select my element: $("*")

When it wasn’t working I started writing to the javascript console to help me debug. I noticed I kept getting 3 events fired for the one mousedown event:

I entered the if and id = undefined
I entered the if and id = square
I entered the if and id = undefined

This was the point I started googling and not getting anywhere. Then, when I had another episode of my recurring “The problem is my thinking and not the code executing incorrectly” epiphanies (I wonder how many of those I’ll have in a lifetime), I asked myself “How many elements are ACTUALLY in the point that I clicked?” Up until this point my unconscious assumption is that when you an event fires (mousedown, click, mouseup, mouseover, etc) it’s only acting on the element you see, i.e. the topmost element. Well it turns out that is not the case! It’s actually firing on everything along the z access of the website! Even though I was clicking on <div id=”square”></div>, that div was sitting inside of and body is inside of “document.” Or at least I’m assuming “document” (as in the page itself) is the third element the event is firing off of. To test my theory I put a wrapper div around the “square” div and low-and-behold, the event fired 4 times!

So I learned my lesson, don’t be lazy and use a wildcard. Not that I would have done it that way in the final product; I was just creating a proof of concept. Thankfully I only spent about an hour on this so that’s not much lost time. There are a few ways I can go about making a better element selector. I can either set an ID for each element and start them all with the same prefix like id=”onTopSquare” and id=”onTopCircle” so that I can select just the ones I want to be able to set on top by doing $("[id ^= onTop]"). Or, and maybe my preferred way, add a class to all the elements I want to be able to do this to and use something like $(".onTop"). I definitely don’t want to just use $("div") because I almost always have nested divs so it will always fire multiple times!

I hope this helps someone since I didn’t get a good explanation from any of the posts I stumbled upon!


3 December 2015

Just had an issue crop up where a web tool we created for data gathering could no longer query AD for membership groups of a computer. It only seemed to be happening on new machines added to the enterprise; old ones continued to work fine. After checking with the team that manages AD to see if they changed anything we were stumped. Couldn’t find anything wrong anywhere with permissions or account locks or anything else.

My C# code was using the FindOne method for DirectorySearcher. All of our hostnames are unique and we only have one domain so how could that be a problem? Well, it turns out something has started causing DNS entries in AD (which I wasn’t aware that we were even using that in AD… DHCP and DNS is actually managed by another product) to return back before computer objects. Perhaps the folks imaging and adding machines to the domain have changed a workflow. When querying the older PCs the computer object gets returned first.

Needless to say my DirectorySearcher now asks for a computer object class. So always make your filters as detailed as possible! (I should know this already….)

If anyone finds this and wants to know if multiple objects are being returned you can use the following powershell code (after importing the ActiveDirectory module):

Get-ADObject -LDAPFilter “(name=HOSTNAME)”


22 September 2015

I know, this title is pretty vague and can cover a lot of different causes but this one didn’t stand out to me at first and I thought I would share it.

Got called by a user at one of the facilities who could authenticate when logging into the machine but all network locations he kept getting the generic resources does exist or you don’t have access error. For example, we redirect our user’s desktops and favorites to a location stored out on a netapp share. When he would login he would get the error above because it was trying to load his desktop but couldn’t.

His credentials were fine and double checking the permissions on the folder showed they were fine, too. After removing his local profile (we don’t use roaming) it still persisted. Odd. Well, it turned out he had domain credentials stored in Credential Manager that were for a service account he had been testing earlier. Don’t get me started about how they got stored… After removing the stored credentials from credential manager everything was fine.

I just find it odd that credential manager isn’t tied to the profile and so persisted after removing the profile. On second thought, I guess it could be because the vault stores passwords and associates them to your SID and on a domain that remains the same even when a local profile is removed since it gets the SID from your domain account.


7 August 2015

I decided to be an early adopter of an OS for once and upgraded to Windows 10 the weekend after it released. It’s not completely awful but it’s also not without it’s headaches. One thing I wanted to post and share was this little bit of info since there doesn’t seem to be anything on the internet about it. After upgrading RDP stopped working and reason why is because of a really awful automatic decision the OS is making for you.

After the upgrade and I connected to my home network again (“for the first time”) it asked if I wanted to allow discovery of other items on the network. I said no. Well, apparently when you say no it automatically sets the category for that network connection to public and Windows won’t allow RDP connections if it’s on a public network. Why it automatically assumes this based on discovery is beyond me. It’s a really dumb move on Microsoft’s part. They should ask you how you want to classify like it did in Windows 7 and 8.

To fix it you have two options:
Manually edit the registry settings under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Profiles\{Whatever GUID you have} and change Category to “1” and Category Type to 0 (that’s what mine currently are and it’s working again.

Or, you can remove the connection (I deleted the GUID key mentioned in the location above) and let it prompt you again. You may have to disable/enable the network adapter before it will do this. You have to tell it to allow discovery when it asks about the network again and it will set it to a Private network.


23 February 2015

I learned something new last Friday as well as had another idea driven home again. I like it when that happens. It’s odd to learn something yet periodically “realize” it all over again when life has need of that lesson.

I started working again on a scripting promise I made a while ago about sharing the ability to run an encrypted script without having to write the unencrypted contents to disk, thus making the contents of your script more secure. I’ve had the concept in my head and most of the proof it could be done, I just have to work on one very irritating issue with arrays.  The other thing I needed to work on, and solved, was generating a random string.  PowerShell has a native cmdlet to get a random number but not one for a string.  I needed a string, so that I could randomly generate the IV that goes with each script.  It’s generally accepted that knowing the IV doesn’t make it less secure so hard coding it to the resultant script isn’t a bad thing.  However, it IS bad to always use the same IV.  I figured it couldn’t be hard to write a simple function to generate a random string; it really wasn’t.  Initially it was frustrating, though.

I started out with the idea I could use the get-random to randomly generate a 0 or 1 and then loop through that 8 times to generate an 8 bit binary code that I could then convert to a string.  Figuring out how to make that 8 bit binary code into a string took a bit, and required multiple conversions of the data.  My searching proved more fruitful than I intended.  The one thing I learned was all of the different conversion methods available via .NET. Specifically, converting characters from decimal/hex/binary to whichever form is needed. It makes sense that they exist now that I think about it, but at the time I had never had a need for them and didn’t know they existed.  This lead me to my recurring life lesson:  it’s most invaluable being able to think differently.  What could have continued to be difficult and maybe (for all intents and purposes) impossible, became an elegant solution just because I started to look at my situation from a different point of view.

I didn’t need to generate a rand byte of 1’s and 0’s because each ASCII character also has a decimal (and hex) value as well.  So instead of using get-random 8 times to produce 1 ASCII char, I just needed to use it once to generate it’s decimal value.  I didn’t want the non-character values in ASCII so I gave it the range of 33-127.  The get-random does not include the max number (127, which is DEL and I don’t want) but it does include the minimum, 33 (which I do want and is !).  I don’t know why it behaves this way.. it doesn’t make any sense.

The final\ script, below, ended up being only half the lines of the previous, unfinished script.  I hope you find it useful.  To use it, you just call the function and pass it the number of characters you want to use.  ex)  create-randomString 32

Function create-randomString {
    param(
    [Parameter(Mandatory=$true,Position=1)]
    [string]$length

)

    $finalString = @()
    For ($i=1; $i -le $length; $i++) {
        $finalString += [CHAR][BYTE](get-random -max 127 -min 33)
    }

    Write-Host (-join $finalString)

}

8 February 2015

Got tired of the old gimmicky design and created something new and simple. Still needs a few tweaks here and there I think but I’m fairly happy with it.

I’m hoping to get images, instead of just text, for the Most Popular items across the top… but we’ll see if/when I get there 🙂


12 January 2015

Update 5-27-17 — This wont work with Seasons 19 and after as they have removed the download link from each episode page.  Also, Revision3 no longer exists (got folded into Seeker, whatever that is), so who knows how long the older episodes from 1-18 will continue to exist.  The actual download mp4 files were/are hosted on the revision3.com domain.

I’ve never been able to find a REALLY good podcatcher. I use Juice and it works good… as long as it’s always running. I thought I had it set to startup when I login but apparently I hadn’t. I fell off keeping up with Hak5 and then one day I go to start up again and I have no current episodes! It had been months. I left it alone again and there I was yesterday wanting them again. The RSS feed only does the current season so I couldn’t use iTunes or another app to “Download All” as they only saw the current season. I needed to go back two seasons, too. Enter PowerShell.

I figured I could do it. Fuddled with Invoke-WebRequest but it doesn’t work great. The Links property was always missing the link to the HD video file. I ended up having to create a WebClient and download the webpage, then parse it for the HD video file. It didn’t take too long… if you don’t count the typo I made which kept making me download the page for episode 1 of season 1, lol (No HD back then…). I added params and comments and spruced it up a bit for general internet consumption.

###################################################################
##  Description:  Downloads all of the HD videos for the given season of Hak5
##
##  This script is provided as is and may be freely used and distributed so long as proper
##  credit is maintained.
##
##  Written by: thegeek@thecuriousgeek.org
##  Date Modified:  01-12-2015
###################################################################

param(
	[Parameter(Mandatory=$true,Position=1)]
	[string]$season,
	
	[Parameter(Mandatory=$true,Position=2)]
	[int]$first,
	
	[Parameter(Mandatory=$true,Position=3)]
	[int]$last
)

# First I need to check if any of the episode numbers are less than 10
# if so, I need to add a 0 in there to get the correct episode numbers.
If ($first -lt 10) { 
	[int]$first = "$($season)0$first"
}
Else {
	[int]$first = "$($season)$first"
}

If ($last -lt 10) { 
	[int]$last = "$($season)0$last"
}
Else {
	[int]$last = "$($season)$last"
}

# Here I just setup paths for easy use later. Be sure to set Drive to your own drive letter!
$saveDir = "Drive:\Podcasts\Hak5"
$seasonDir = "$saveDir\Season $season"
$url = "http://hak5.org/episodes/hak5-"

# Keeping directory structure.  I test to see if the folder "Season $season"
# exists and if it doesn't it creates it.  The web client will not create it.
If (!(Test-Path $seasonDir)) {
	New-Item $seasonDir -ItemType Directory
}

# Use the first and last episode numbers to go through a loop.  Each one
# is used to create the final URL that will be downloaded for it's source code.
# Thankfully Hak5 is organized and this is easy to do!
For ($i = $first; $i -le $last; $i++) {
	write-host "Working on episode $i"
	$wc = new-object system.net.webclient
	$wc.DownloadFile("$($url)$i","TempLocation")    # I build the URL using the base path I had above and combine it with the episode number from the current iteration of the loop
	$sourceCode = get-content TempLocation    # Be sure to make TempLocation your own path!
	ForEach ($line in $sourceCode) {
		$boolResponse = $line -match "http(.*?)hd720p30\.h264\.mp4"    # This regex expression is saying match a string http (with anything here in the middle) and is followed by hd720p30.h264.mp4
		If ($boolResponse) {
			$fileURL = $matches.0     # the system variable $matches keeps record of all matches you've made.  The query above gives two results, the first is the one I want (complete URL).
			$fileName = $fileURL.Split("/")[$_.Length - 1]     # I need a filename to save as, so I grab the one out of the URL
			$wc.DownloadFile($fileURL,"$seasonDir\$fileName")
		}
	}
}

And then to call it, run the script with parameters such as: DownloadHak5Season.ps1 16 1 26, which tells it to download season 16, episodes 1-26