Copying a File from Within a Remote Powershell Session

Recently I needed a way to copy a certificate file from within a PowerShell session to another Windows machine without opening a nested PowerShell session.

Here’s my setup:

  • A Windows 10 laptop, from which I’m remoting
  • NC1, a Server 2016 virtual machine I’m remoted into. It’s a member of a domain.
  • HYPERV1, the Server 2016 machine I want to copy a certificate file to. It’s not a member of a domain.

I execute all of the following commands on NC1, the VM I’m remoted into.

Here’s the first thing I tried. The HYPERV1 machine is not a member of a domain, so the following doesn’t work:
$ Copy-Item .\nccert.cer \\hyperv1\c$
Access is denied
+ CategoryInfo          : NotSpecified: (:) [Copy-Item], UnauthorizedAccessException
+ FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.CopyItemCommand

What about specifying the -Credential parameter? That doesn’t work either.
$ Copy-Item .\nccert.cer \\hyperv1\c$ -Credential hyperv1\administrator
The FileSystem provider supports credentials only on the New-PSDrive cmdlet. Perform the operation again without specifying credentials.
+ CategoryInfo          : NotImplemented: (:) [], PSNotSupportedException
+ FullyQualifiedErrorId : NotSupported

And that error pretty much tells me what I need to do: use the New-PSDrive cmdlet!
$ New-PSDrive -Name H -PSProvider FileSystem -root \\hyperv1\c$ -Credential hyperv1\administrator
$ Copy-Item .\nccert.cer h:\
$ Remove-PSDrive -Name H

How to Fix the Blurry, Fuzzy, Ugly Text in Windows 10

After upgrading my Lenovo ThinkPad to Windows 10, I was so pumped. The upgrade went smoothly, all my apps worked, but then I noticed something: some apps had blurry, fuzzy text.

Ugly, blurry, fuzzy text on Windows 10:

vSphere-on-Windows-10-with-fuzzy-text

This might not bother some people, but to me it felt like trying to read a wet book with my glasses off. Most everything else looked sharp and normal, so I knew it wasn’t a native resolution or global DPI scaling issue, which is what most of my Google-fu turned up.

The fix (hint: not prescription eyeglasses)

The fix turned out to be crazy stupid. Well, more stupid than crazy. Go into the Properties of the app that’s rubbing salt water in your eyes:

vSphere-on-Windows-10-properties

Navigate to the Compatibility tab. Check that “Disable display scaling on high DPI settings” check box, apply the settings, then launch the app again.

vSphere-on-Windows-10-with-sharp-text

That’s what I’m talking about. The window is bigger, and the text doesn’t look like garbage.

But wait, there’s more! (PowerShell)

If you’re a PowerShell 1337 scripter, you may run into a similar issue. Check this out:

PowerShell-with-ugly-fuzzy-blurry-font

If you’ve ever hooked up a computer to an old CRT television using an RF converter, well, this is about what it looks like. Ugly as homemade sin, as they used to say. Don’t worry about the error. I left it to highlight how horrendously eye-stab-worthy this console looked when I first opened up a can of PoSH on my newly minted Windows 10 upgrade.

The fix? Go to PowerShell properties:

PowerShell-on-Windows-10-properties

Navigate to the Options tab (intuitive, right?). Check the box “Use legacy console” (duh), then apply the settings. Relaunch PowerShell.

PowerShell-with-sharp-font

The image makes it look a bit fuzzy still, but on my screen it looks crisp and sharp.

Creating a File Share with PowerShell and Windows Server Core

Sometimes you just need to create a file share.

With Windows Server Core, you don’t have all the old GUI tools that we’re all used to. So you have to make do with PowerShell and the old fake DOS prompt. Fortunately, with a little help, it’s pretty easy.

First, create the folder you want to share. In this case, c:\share

Next, modify the ACL to grant the DOMAIN\File Server Admins group full control

$sharepath = "c:\share"
$Acl = Get-ACL $SharePath
$AccessRule= New-Object System.Security.AccessControl.FileSystemAccessRule("DOMAIN\File Server Admins","full","ContainerInherit,Objectinherit","none","Allow")
$Acl.AddAccessRule($AccessRule)
Set-Acl $SharePath $Acl

Finally, create the share and grant everyone full access.
NET SHARE sharename=c:\share  "/GRANT:Everyone,FULL"

Done.

Windows Server Core Full Configuration with PowerShell

How to Configure Server Core with Active Directory Services, DNS, and DHCP Using Nothing But PowerShell

Windows Server 2012 offers two installation options: Server Core or “Server with a GUI”. This begs the question: Why would you want to install Server Core instead of the GUI? One reason may be that you have limited physical hardware resources and want to keep the footprint as small as possible.

Recently I needed to build a domain controller, DHCP, and DNS server for a branch office. This office has a Riverbed Steelhead WAN optimization appliance which runs a nested VMware ESXi hypervisor. The appliance has limited memory and disk space, so I needed to keep the installation as small as possible (Incidentally, if I only needed DNS and DHCP, I would have just installed RedHat Enterprise Linux, but having the server be an Active Directory domain controller was also a requirement.)

I’m going to show you step-by-step how I configured Active Directory Services, DNS, and DHCP on a Windows Server 2012 Server Core installation. Continue reading

Use PowerShell to Find Citrix ICA Client Versions In Use On a XenApp 6 Farm

In many Citrix environments it’s common to have a large variety of ICA client versions. One thing that sometimes surprises users and IT folks alike is how much of a performance increase can be seen after upgrading an old ICA client. But how do you know which clients need upgrading?

One of the biggest challenges has been deciphering what ICA client version the cryptic “client build number” in a user’s session information translates to. Well thanks to the XenApp 6 PowerShell SDK, we can easily find Citrix ICA client version information in a snap with the following script:

#################################################
# XenApp 6 Client Version Retrieval Script
# Created by Ben Piper
# http://benpiper.com, [email protected]
#################################################

function convertToVersion($build) {
	switch($build){
		6685 {"13.0"; break}30 {"12.1"; break}6 {"12.0.3"; break}6410 {"12.0"; break}142{"Java"; break}317{"3.0"; break}324{"3.0"; break}330{"3.0"; break}349{"3.0"; break}304{"MAC 6.3"; break}314{"MAC 6.3"; break}323{"MAC 6.3"; break}326{"MAC 6.3"; break}400{"MAC 7.0"; break}402{"MAC 7.0"; break}405{"MAC 7.0"; break}406{"MAC 7.0"; break}407{"MAC 7.0"; break}402{"MAC 7.0"; break}411{"MAC 7.0"; break}500{"MAC 7.1"; break}600{"MAC 10.0"; break}601{"MAC 10.0"; break}581{"4.0"; break}606{"4.0"; break}609{"4.0"; break}614{"4.0"; break}686{"4.0"; break}715{"4.2"; break}727{"4.2"; break}741{"4.2"; break}779{"4.21"; break}730{"wyse1200le"; break}910{"6.0"; break}931{"6.0"; break}961{"6.01"; break}963{"6.01"; break}964{"6.01"; break}967{"6.01"; break}985{"6.2"; break}986{"6.2"; break}1041{"7.0"; break}1050{"6.3"; break}1051{"6.31"; break}1414{"Java 7.0"; break}1679{"Java 8.1"; break}1868{"Java 9.4"; break}1876{"Java 9.5"; break}2600{"RDP 5.01"; break}2650{"10.2"; break}3790{"RDP 5.2"; break}6000{"RDP 6.0"; break}2650{"10.2"; break}5284{"11.0"; break}5323{"11.0"; break}5357{"11.0"; break}6001{"RDP 6.0"; break}8292{"10.25"; break}10359{"10.13"; break}128b1{"MAC 10.0"; break}12221{"Linux 10.x"; break}13126{"Solaris 7.0"; break}17106{"7.0"; break}17534{"7.0"; break}20497{"7.01"; break}21825{"7.10"; break}21845{"7.1"; break}22650{"7.1"; break}24737{"8.0"; break}26449{"8.0"; break}26862{"8.01"; break}28519{"8.05"; break}29670{"8.1"; break}30817{"8.26"; break}31327{"9.0"; break}31560{"11.2"; break}32649{"9.0"; break}32891{"9.0"; break}34290{"8.4"; break}35078{"9.0"; break}36280{"9.1"; break}36824{"9.02 WinCE"; break}37358{"9.04"; break}39151{"9.15"; break}44236{"9.15 WinCE"; break}44367{"9.2"; break}44376{"9.2"; break}44467{"Linux 10.0"; break}45418{"10.0"; break}46192{"9.18 WinCE"; break}49686{"10.0"; break}50123{"Linux 10.6"; break}50211{"9.230"; break}52110{"10.0"; break}52504{"9.2"; break}53063{"9.237"; break}55362{"10.08"; break}55836{"10.1"; break}58643{"10.15"; break}				
		default {$build; break}
	}
}

$clientUsage = @()
foreach($session in Get-XASession -full | where {$_.state -eq "Active" -and $_.protocol -eq "Ica"}) {
	$clientUsage += new-object psobject -Property @{
		User = $session.accountname
		Workstation = $session.clientname
		Client = convertToVersion($session.clientbuildnumber)
	}
}

$clientUsage | sort-object -Property Workstation | get-unique -asstring

The script makes use of build-to-version information gleaned from Nick Holmquist’s ICA Client Build List and the Citrix Client Build List thread on Citrix Forums (Thanks!). The convertToVersion function uses the PowerShell switch command to identify and then return the ICA client version based on the build number. If the key-value pair is not in the list, the build number is returned. All ICA client versions in the list have at least one decimal place to make it easy to distinguish build numbers from client versions.

Download the script here

Instantly Publishing Citrix Apps to Individual Servers Using PowerShell

Sometimes there is a need to publish an application to individual XenApp servers for baselining or troubleshooting purposes. But if you have a lot of published applications in your XenApp 6 farm, this can be a huge hassle. Thankfully PowerShell allows us to quickly and easily take our existing published applications and automatically create individually published apps for each server. We can use Worker Groups to control which servers get an individually published application. Not only that, we can organize the applications neatly by creating separate console and Client folders for each server. I wrote this script to accomplish all of the above using XenApp 6 PowerShell SDK’s powerful features:

$appFolder = "Applications"
$testfolderpath =  "Applications/Test/Single Server Test"
$sourceAppPath = "Applications/Test"	#apps in the root of this path will not be copied
$publishTo = "DOMAIN\GroupWithAccessToIndividualServers"
$targetWorkerGroup = "Main Farm Servers"
$clientFolder = "Test\Single Server Test"
$WhatIfPreference = $false		# Simulate script but do not execute any changes (will yield errors)

# Main Program

$servers = Get-XAServer -workergroupname $targetWorkerGroup
$sourceFolders = Get-XAFolder -folderpath $appFolder -recurse | where {$_ -notmatch $testfolderpath -and $_ -match $sourceAppPath -and $_ -ne $sourceAppPath}

# Create folder for each server
foreach ($server in $servers) {						
	#generate folder name for each server
	$createfolder = $testfolderpath+"/"+$server.servername
	#create folder for each server
	new-xafolder -folderpath $createfolder
	foreach ($folder in $sourceFolders) {
		#get list of apps to copy in each folder
		$appstocopy = Get-XAApplication -folderpath $folder			
		foreach ($app in $appstocopy) {
			## generate name for new app
			$newname = $app.displayname+" "+$server.servername
			$newclientfolder = $clientfolder+"\"+$server.servername
			## get existing accounts to remove			
			$accountsToRemove = ($app | Get-XAApplicationReport).accounts
			## copy application to server's folder		
			$newapp = $app | Copy-XAApplication -folderpath $createfolder
			## publish application to lone server		
			$newapp | set-xaapplication -servernames $server.servername -clientFolder $newclientFolder
			## remove existing accounts
			foreach ($account in $accountsToRemove) {
				$newapp | remove-xaapplicationaccount -Accounts $account
			}
			## publish application to AD group
			$newapp | add-xaapplicationaccount -Accounts $publishTo
			## rename new application
			$newapp | rename-xaapplication -newdisplayname $newname						
		}}}

Download the script from GitHub.

Just set the variables with what is appropriate for your environment and run the script.
The $appFolder variable is the root path of your applications (default is “Applications”)
$testfolderpath is the full path of the folder where the applications published to individual servers will be published (no trailing slash)
Set $sourceAppPath one level higher than the path of the folder containing the applications you wish to copy. The script will recurse down into child folders from this path, but will not copy applications that reside within this path.
$publishTo is a string containing the AD users or groups the newly published applications should be published to
$targetWorkerGroup is the name of the worker group containing the servers the applications will be published to
$clientFolder is the root Program Neighborhood/Client folder that the individual server folders will be created under

The once-daunting and time-consuming task of copying and modifying applications for each server can now be done automatically in a matter of minutes!

Update: It’s even easier to undo the changes made by the above script if you decide you want to scrap the newly published apps and folders and start over. Just use the following script:

$testfolderpath =  "Applications/Test/Single Server Test"
$WhatIfPreference = $false

# Main Program
(get-xaapplication -folderpath (Get-XAFolder -folderpath $testfolderpath)) | remove-xaapplication
(get-xafolder -folderpath $testfolderpath) | remove-xafolder

How To Get a Unique STA ID for each of your PVS Provisioned XenApp Servers

Citrix Provisioning Services is very nice, but it does come with a slightly annoying quirk: All of your provisioned XenApp servers end up with the same STA ID! This will cause all sorts of problems for Citrix Access Gateway, Citrix Receiver, and anything else that may depend on having unique STA IDs. The good news is that fixing this little problem is easier than you might think.

To resolve the duplicate STA ID issue, we’ll do the following:
1. Create personality strings in Provisioning Services for each XenApp server
2. Put a PowerShell script on our golden image
3. Create a startup task to execute the PowerShell script

Let’s begin:

The format of the STA ID is simple. It is “STA” followed by the MAC address of the XenApp server’s NIC. The STA ID can really be anything beginning with “STA”, so you could get creative and have “STABLEFLY”, “STANK”, “STALE”, “STARTBUTTON”, “STACKOVERFLOW”.. and the list goes on. But I recommend sticking with the MAC address because it’s unique (usually), and is easy to match up.

1. In Provisioning Services, create a personality string for each server with the Name “UID” and the String “STA001122DDEEFF”, substituting the MAC address of the server for the hex I just threw in there.
Define Personality Strings

2. Copy this PowerShell script to your scripts location on your golden image: (Note: Download the file from the above link and do not copy and paste the text below, otherwise PS will complain.)

# STA Replacement Script for Citrix Provisioned Servers
# Created 7-25-11 by Ben Piper (email: [email protected], web: http://benpiper.com )
# Get UID string
# Replace STA ID
# Restart CTXHTTP service

$stastr = get-content C:\Personality.ini | Select-String -Pattern “UID=STA”
$CtxStaConfig = Get-Content ‘C:\Program Files (x86)\Citrix\system32\CtxSta.config’ | ForEach-Object {$_ -replace ‘^UID=.+$’, $stastr}
$CtxStaConfig | Set-Content ‘C:\Program Files (x86)\Citrix\system32\CtxSta.config’
Stop-Service CtxHTTP
Start-Service CtxHTTP

3. Create a scheduled task to execute the PowerShell script at startup. Make sure the account that will be executing the script has appropriate permissions.

If you are not already running PowerShell scripts, you’ll need to set the ExecutionPolicy on your gold image to Unrestricted by issuing the cmdlet ” Set-ExecutionPolicy Unrestricted” at a PS prompt.

I recommend testing the script first on your Master Target server before deploying it farm-wide. The script will still work as long as you have a personality string defined for your Master Target server, even if the vDisk is in Private mode.

How To Create XenApp6 Published Applications for Test In Just Seconds Using PowerShell

Prerequisite: XenApp6 PowerShell SDK

Let’s say you want to copy all currently published applications into a folder named “Test” in the console tree, while simultaneously modifying the new published apps with different permissions and client folder settings. Here’s how:

First, create the “Test” folder by hand (you can use New-XAFolder -FolderPath Applications\Test if you are so inclined), then use the following command to copy the applications into it:

get-XAApplication | Copy-XAApplication  -folderpath Applications\Test

Second, modify the published application properties to set the client folder (what folder the applications show up under in Program Neighborhood or Web Interface), and the permissions in one fell swoop. We’ll call the client folder “Test” and publish to the groups “domain1\citrix admins” and “domain2\Test Users”:

Get-XAApplication -folderpath Applications\Test | set-xaapplication -clientfolder Test -accounts "domain1\citrix admins","domain2\Test Users"

That’s it! Now doesn’t that beat right-click -> “Duplicate Application”?

Citrix XenApp6 0x80060016 Error In PowerShell

I ran into a little snag when executing some XenApp PowerShell commands. Certain commands like Get-XAFarm and Get-XAAdministrator would always give an “0x80060016” error. Here is an example and the fix:

PS C:\Windows\system32> Get-XAFarm
Get-XAFarm : Error reading the current administrator data (0x80060016)
At line:1 char:11
+ Get-XAFarm <<<<
+ CategoryInfo : InvalidResult: (:) [Get-XAFarm], CitrixException
+ FullyQualifiedErrorId : GetCitrixAdminType,Citrix.XenApp.Commands.GetFarmCmdlet

Typically this error code in Citrix indicates a problem with IMA. But in this case it was even simpler than that: IMA couldn’t resolve the hostname of the database server hosting the data store. Make sure that the correct DNS suffixes are being applied so IMA can find the server, and if that fails, just add it to the hosts file and try again.