Convert Citrix PVS Mcli-Get Output To Objects
Back in late 2009, I wrote a series of posts about the Citrix Provisioning Services’ PowerShell Snapin. 3,5 years later, even within the latest version of PVS the cmdlets return structured text output instead of “real” objects. I’m still hoping that Citrix will provide us a PVS Module/Snapin that follows the common PowerShell standards.
Whatever, today I want to share a generalized version of my function that converts a text array (Mcli-Get output) to PowerShell/.NET objects. For more background information and explanation how the function below works read my former blog post Citrix Provisioning Services 5.1’s PowerShell Interface, Part III
function ConvertTo-PvsObject
{
<#
.SYNOPSIS
Converts the output of Mcli-Get from text array to regular objects with properties.
.DESCRIPTION
The Citrix Provisioning Services cmdlets return text arrays instead of .NET objects.
This function takes the output of a given Mcli-Get command and turns it into
"PVS objects" with properties.
.PARAMETER InputObject
The output of a Mcli-Get command
.EXAMPLE
PS C:\> Mcli-Get DiskInfo | ConvertTo-PvsObject
.EXAMPLE
PS C:\> $diskinfo = Mcli-Get DiskInfo
PS C:\> ConvertTo-PvsObject $diskinfo
#>
[cmdletBinding(SupportsShouldProcess=$False)]
param (
[Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
$InputObject
)
process {
switch -regex ($InputObject) {
"^Record\s#\d+$" {
if ($record.Count) {
New-Object PSObject -Property $record
}
$record = @{}
}
"^\s{2}(?<Name>\w+):\s(?<Value>.*)" {
$record.Add($Matches.Name, $Matches.Value)
}
}
}
end {
if ($record.Count) {
New-Object PSObject -Property $record
}
}
}
Disclaimer: I hope that the information in this post is valuable to you. Your use of the information contained in this post, however, is at your sole risk. All information on this post is provided “as is”, without any warranty, whether express or implied, of its accuracy, completeness, fitness for a particular purpose, title or non-infringement, and none of the third-party products or information mentioned in the work are authored, recommended, supported or guaranteed by me. Further, I shall not be liable for any damages you may sustain by using this information, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such damages.
Good Read About Windows Azure “Cloud VMs”
On simple talk, I stumbled upon a nice intro to Windows Azure Virtual Machines (WAVM):
Windows Azure Virtual Machine: A Look at Windows Azure IaaS Offerings (Part 1)
Windows Azure Virtual Machine: A Look at Windows Azure IaaS Offerings (Part 2)
Login VSI Benchmarking Suite Reaches New Maturity Level
Login Virtual Session Indexer, the core product of my dutch friends at Login VSI B.V., goes Version 4.0. It will be available as from tomorrow, that is May 2, 2013.
How time flies! I clearly remember how sometime in 2008/2009 a small team of brainiacs in the lab of Login Consultants started to build the first version of Login VSI. Their intention was to finally address the increasing demand in VDI and SBC projects for a tool that helps to measure performance, impact, and scalability of different infrastructure options. The need for such a benchmarking tool was really urgent as other existing tools were (and are) too expensive, too complicated and not vendor-independent. After one, two years of development Login VSI made its breakthrough: shortly after an Express edition of the tool had been made available for free it was downloaded thousands and thousands of times and thus became a well-known benchmarking tool for VDI and SBC environments. Quite rightly Login VSI claims to be the “de facto industry standard”. As of this blog post Login VSI has been adopted by vendors, by system integrators, by service providers as well as eventually you? (See also the list of Whitepapers based on testing with Login VSI.)
Enough storytelling. What’s new in the new major release of Login VSI? In a nutshell: It is easier to setup and integrate; it is easier to create tests; it simulates real world users in a more realistic way. Login VSI is better than ever! A lot of effort has been put into the new release. It is improved in every respect, from A to Z.
Since a picture paints a thousand words, I’ve prepared a couple of screenshots below. Skimming over the gallery you may will notice that Login VSI v4 has a new, more intuitive GUI which includes a wizard that helps to create and configure tests, an integrated workload editor, and a new dashboard that displays real-time testing results/progress. So much for the outer appearances. But where do the inner values come in? Version 4 introduces a new meta language for easier workload customization. In order to improve the test realism, for one thing the duration of the standard workloads has been increased from 14 to 48 minutes loop and for another thing the datasets now offer 1000 different documents per type, more and larger websites, and a video library in every format. This ensures a far more realistic simulation of a real world variety in data usage.
- Login VSI 4.0 Management Console Home
- Login VSI 4.0 Management Console AD Setup
- Login VSI 4.0 Management Console Add Launchers
- Login VSI 4.0 Management Console Add Launcher Wizard
- Login VSI 4.0 Management Console Workload Settings
- Login VSI 40 Management Console Workload Customization
- Login VSI 4.0 Management Console Scenario
- Login VSI 4.0 Management Console Connection Wizard
- Login VSI 4.0 Management Console Start Test
- Login VSI 4.0 Management Console Dashboard
- Login VSI 4.0 Workloads
- Login VSI 4.0 Analyzer VSImax
- Login VSI 4.0 Analyzer VSImax Detailed
- Login VSI 4.0 Analyzer Scatterchart
- Login VSI 4.0 Analyzer Scatterchart By Time
- Login VSI 4.0 Analyzer Logontimer
- Login VSI 4.0 Analyzer Compare Wizard
Get-Acl: Show Users Who Are Member Of (Nested) Groups, #PowerShell
From time to time, customers charge me to report about file access rights from the user account perspective, meaning a summary regarding the allowed and the denied file system accesses per user. Typically, administrators implement role-based access control (RBAC) using nested groups. Nested groups simplify the management of file system access and security audits. Individual user account only acquire access through group memberships that correspond with their business role (see also AGDLP). So much for theory! Over time, more and more exceptions prove the rule, and user accounts acquire access to file system ressources out of the RBAC concept. A few lines of PowerShell can help to distinguish between the good and bad apples.
The function below, Get-ResolvedAcl, leverages the ActiveDirectory module’s Cmdlets Get-Acl (to list explicit allow/deny access), Get-ADObject (to identify the objectClass of an Access Control Entry), and Get-ADGroupMember (to list the members of a group). Furthermore, a sub function called Get-ADNestedGroupMember calls Get-ADGroupMember recursively in order to identify user accounts in nested groups.
function Get-ResolvedAcl
{
[cmdletBinding(SupportsShouldProcess=$false)]
param (
[Parameter(Position=0, Mandatory=$true)]
[string]
$Path
)
function Get-ADNestedGroupMember ($Group)
{
Get-ADGroupMember -Identity $Group | ForEach-Object {
$ADObjectName = $_.name
switch ($_.ObjectClass) {
'group' {
Get-ADNestedGroupMember -Group $ADObjectName
}
'user' {
@{UserName=$ADObjectName;Group=$Group}
}
}
}
}
Import-Module -Name ActiveDirectory
(Get-Acl -Path $Path).Access | Where-Object {$_.IsInherited -eq $false} | ForEach-Object {
[string]$Trustee = $_.IdentityReference
$UserDomain = $Trustee.Split('\')[0]
$SamAccountName = $Trustee.Split('\')[1]
$ADObject = Get-ADObject -Filter ('SamAccountName -eq "{0}"' -f $SamAccountName)
switch ($ADObject.ObjectClass) {
'group' {
$NestedUser = Get-ADNestedGroupMember -Group $SamAccountName
if ($NestedUser) {
foreach ($User in $NestedUser) {
$UserName = '{0}\{1}' -f $UserDomain, $User.UserName
$GroupName = $User.Group
@{
UserName=$UserName;
GroupName=$GroupName;
DirectAccess=$false;
FileSystemRights=$_.FileSystemRights;
AccessControlType=$_.AccessControlType
}
}
}
}
'user' {
@{
UserName=$Trustee;
GroupName='';
DirectAccess=$true;
FileSystemRights=$_.FileSystemRights;
AccessControlType=$_.AccessControlType
}
}
}
}
}
Disclaimer: I hope that the information in this post is valuable to you. Your use of the information contained in this post, however, is at your sole risk. All information on this post is provided “as is”, without any warranty, whether express or implied, of its accuracy, completeness, fitness for a particular purpose, title or non-infringement, and none of the third-party products or information mentioned in the work are authored, recommended, supported or guaranteed by me. Further, I shall not be liable for any damages you may sustain by using this information, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such damages.
Set AD User Profile Paths (Roaming and RDS) #PowerShell
Hello,
with this post I show how to set the paths of the Roaming Profiles and the Remote Desktop Services (RDS) Profiles, formerly known as Terminal Services (TS) Profiles, in a set of Microsoft Active Directory user accounts.
The easy part is the Roaming Profile path. You just need to leverage the ActiveDirectory PowerShell module from RSAT:
Import-Module ActiveDirectory
$Filter = <define a filter for Get-ADUser here>
$Path = <define the roaming profile path here>
Get-ADUser -Filter $Filter | ForEach-Object {
Set-ADUser $_ -ProfilePath $Path
}
The RDS Profile is not that easy. It’s easy too though. You just need to leverage ADSI in order to set the RDS Profile.
Import-Module ActiveDirectory
$Filter = <define a filter for Get-ADUser here>
$Path = <define the roaming profile path here>
Get-ADUser -Filter $Filter | ForEach-Object {
$ADSI = [ADSI]('LDAP://{0}' -f $_.DistinguishedName)
try {
$ADSI.InvokeSet('TerminalServicesProfilePath',$Path)
$ADSI.SetInfo()
}
catch {
Write-Error $Error[0]
}
}
Disclaimer: I hope that the information in this post is valuable to you. Your use of the information contained in this post, however, is at your sole risk. All information on this post is provided “as is”, without any warranty, whether express or implied, of its accuracy, completeness, fitness for a particular purpose, title or non-infringement, and none of the third-party products or information mentioned in the work are authored, recommended, supported or guaranteed by me. Further, I shall not be liable for any damages you may sustain by using this information, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such damages.
WSUS for XenApp?
“Is there something like WSUS for XenApp, or is it possible to extend Microsoft WSUS in order to download and apply updates and hotfixes to our XenApp servers?” No. But you can try my simple XenApp hotfix deployment framework that I want to share with you today.
Introduction
While (Cloud) service providers have evolved (or are evolving) an ideal approach to maintain their XenApp server farms there are uncountable companies across all sizes that are running one or more XenApp farms w/o a proper maintenance concept. Plenty of these companies indeed have service time windows with corresponding tasks. But quite often the implementation lacks class meaning that at the worst the admin updates each and every damned (sorry) XenApp server by hand.
A year ago or so, I developed a bunch of batch files that build a simple script framework for automated XenApp hotfix installation. My goals were to build a solution that supports the broadest range of XenApp farms as possible on the one hand and to build a “learning” framework that identifies new hotfixes in the source folder tree on its own (and releases the admin from the challenge to script). Therefore I chose Batch scripting and ini files instead of PowerShell and XML. Furthermore my solution either relies on MFCOM (API for legacy XenApp before version 6) nor it uses XenApp PowerShell Cmdlets. It just leverages cmd.exe and some standard commands that you’ll find on every Windows Server with Terminal Services or Remote Desktop Services. In short, the XenApp hotfix deployment framework is designed to update XenApp 4.5/5.0/6.0/6.5 on Windows Server 2003/2003 R2/2008/2008 R2.
Simple means basic. Please don’t expect a superduper allrounder solution. The script framework cares about the sessions though (if you want), gives them some (configurable) grace time and repeatedly sends a warning message. And it disables logons as well. This seems quite a lot but in a perfect world you would like a solution that monitors itself and stops processing on all remaining servers in case of fault for example.
Requirements
The XenApp hotfix deployment framework just needs storage space on a central folder share. The scripts and related files on its own allocate less than 200 KB, but you need to take the hotfix files into account as they’ll be stored inside the framework’s folder structure as well.
Strictly speaking, a dedicated active directory account that runs the maintenance process is not really required. But it is recommended to create such an account, especially if you opt for an automated invocation of the maintenace process via scheduled task for example.
Give the account that runs the maintenance appropriate access on the above mentioned share. Of course the account needs administrative access on the XenApp servers to be able to install the hotfixes successfully.
That’s all, nothing more required from a technical perspective.
Overview
The XenApp hotfix deployment framework consists of a main script, a bunch of library scripts, one or more config files, a folder structure, and – last but not least – a naming convention with a hidden sense (below more).
The main script, XenAppMaintenance.cmd, initializes and controls the progress of the maintenance work.
The config file, Settings.ini, includes changeable global settings that configure logging and session warning timeframe for example. Additional config files (per XenApp Server) can be placed aside the Settings.ini in order to overwrite to common settings for whatever reason or purpose.
The folder structure separates the framework components. This helps the admin to locate easily a config file or to save a new hotfix for example.
The naming convention for additional config files, for library scripts, and hotfix subfolders make up an important part of the framework. For example a part of a hotfix subfolder name is tied to the library script that the framework shall use in order to install the hotfix inside that folder.
Setup
This section outlines the steps to configure the XenApp hotfix deployment framework for your XenApp Farm:
- Create an Active Directory user (optional)
- Create a directory on a file server and share it.
- Apply appropriate NTFS and share security.
- Download and unzip the XenApp hotfix deployment framework to the shared folder
- Open the file\Config\Settings.ini in order to configure settings (optional).
- Download hotfixes from www.citrix.com. For each hotfix, create a subdirectory in the Source folder of the framework. A hotfix subdirectory consists of three parts separated by an underscore: three-digit installation order number, action library script, and optionally a comment. For example, the subdir “010_InstallMsp_CTX126679″ stands for order number 10, use action script “InstallMsp” to install the hotfix, and article number CTX126679.
Invoke Maintenance
This is simple. Just double click XenAppMaintenance.cmd or create a scheduled task.
Disclaimer: I hope that the information in this post is valuable to you. Your use of the information contained in this post, however, is at your sole risk. All information on this post is provided “as is”, without any warranty, whether express or implied, of its accuracy, completeness, fitness for a particular purpose, title or non-infringement, and none of the third-party products or information mentioned in the work are authored, recommended, supported or guaranteed by me. Further, I shall not be liable for any damages you may sustain by using this information, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such damages.
Citrix Provisioning Services: Reverse Imaging a XenApp 6.5 vDisk
This post outlines the steps to build a Reverse Image of a Citrix Provisioning Services vDisk with XenApp 6.5 Server to a VM with locally attached hard disk.
Whenever you have to install or upgrade software on a vDisk that interferes directly with the Citrix Provisioning Services infrastructure you need to choose the Reverse Imaging approach. The installation of such software would fail within a system that was booted either from the vDisk in Private mode or from a Maintenance version of the vDisk.
Reverse Imaging means booting from vDisk in order to capture the vDisk’s contents to a local hard drive. Afterwards you can boot from the local hard drive an make the changes without any of those issues that would arise if you’d have tried to change the vDisk directly.
Reverse Imaging is a time-consuming task. Normally you’ll capture a new vDisk following the Reverse Imaging process. In total you should plan a man-day including preparation, performing the Reverse Imaging, applying the updates, and capturing a new vDisk. Therefore, I recommend to start with it just at the beginning of the work day.
- Dedicate or create a VM and PVS target device for the Reverse Imaging process. (For istance, an already existing update VM/target device for vDisk maintenance purposes would be a perfectly possible candidate for Reverse Imaging.)
- Create and attach a new local hard disk to the Reverse Imaging VM that matches at least the size of the vDisk. (The HD size can be greater though, meaning that with Reverse Imaging you can’t only update software like VMware Tools but also increase the vDisk size as a side benefit.)
- Prepare the source vDisk using one of this options:
- Create a new version of the vDisk and keep the predefined mode, that is “Maintenance” (PVS 6 style)
- Copy the vDisk and put it in Private mode (PVS 5 style)
- Check or set the following target device properties:
- Boot from: vDisk
- Type: Maintenance
- Double-check the target’s vDisk assignment. (Use the vDisk prepared in the third step)
- Boot the target VM from vDisk and log on
- Launch the disk management mmc snap-in.
- Initialize the new local hard disk. (Keep default settings.)
- Partition and format the hard disk. Keep the default disk type (Basic). The partition needs to be either a simple volume or at least a primary partition. (PVS reverse imaging doesn’t support both extended partitions and dynamic disks.)
- Launch the XenApp Server Role Manager and leave the current farm.
- Use the PVS Device Image Builder to invoke the reverse imaging process. (%ProgramFiles%\Citrix\Provisioning Services\BNImage.exe)
- Image Builder will automatically reboot Windows. Log on to let Image Builder finish building the reverse image.
- Once again, the Image Builder will automatically reboot the target. Log on and confirm a message box saying “The device image build is complete”
- Launch the disk management mmc snap-in and mark the partition of the new local hard disk as active.
- In the PVS console, change the target’s “Boot from” setting to “Hard Disk”
- Reboot the target and apply the required changes, updates, whatever..
- Prepare capturing of a new vDisk with PVS Imaging Wizard, reboot, and log on to let XenConvert building the vDisk.
- Close XenConvert to let Windows finalize the logon process.
- In the PVS console, change the target’s “Boot from” setting to “vDisk”. Leave the vDisk in Private mode
- Shutdown the target device VM.
- Detach the local hard disk that was used to hold the Reverse Image.
- Boot the target device VM and log on.
- Launch the XenApp Server Role manager to prepare this server for imaging. (As the server already left the farm in step 8 you need to deselect the corresponding option.)
- Shutdown
- In the PVS console, change the new vDisk’s mode to Standard and configure the relevant targets to boot from this vDisk.
- Clean up. For example you won’t ever use again both, the local hard disk created in step 2 and the Private mode vDisk or vDisk Maintenance version prepared in step 3.
Disclaimer: I hope that the information in this post is valuable to you. Your use of the information contained in this post, however, is at your sole risk. All information on this post is provided “as is”, without any warranty, whether express or implied, of its accuracy, completeness, fitness for a particular purpose, title or non-infringement, and none of the third-party products or information mentioned in the work are authored, recommended, supported or guaranteed by me. Further, I shall not be liable for any damages you may sustain by using this information, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such damages.
How To Copy a Versioned vDisk in Citrix Provisioning Services 6.x
Back in the days of Citrix Provisioning Services 5.x it was common practice to copy an existing vDisk mostly due to maintenance reasons in order to apply updates, install new software and so on and so forth. On the contrary, sometimes the copy of a vDisk was needed as a starting point to create a new vDisk for other purposes (introduction of a new XenApp application silo/worker group for example).
Aside from the fact that Citrix introduced with PVS 6.0 a more flexible and robust vDisk updating approach based on VHD chaining, the good old copy approach still works. In the case of an unversioned vDisk the procedure is as straightforward as in PVS 5.x (copy/label the according *.vhd and *.pvp files, then choose ‘Add or Import Existing vDisk’ in the context menu of the vDisk Store).
If you go the same path with a versioned vDisk (copy the complete VHD cain) you’ll get an error message. Is it impossible to copy a versioned vDisk after all? No, you have two options: merge and export/import
Merging the VHD chain of the source vDisk seems to be the most obvious preparation step in order to copy the vDisk since it results in a single set of VHD/PVP files, meaning that afterwards you’re able to copy that vDisk as simple as ever. But to merge means to commit oneself to a single source vDisk version. You’ll lose flexibility
If you need to keep the source vDisk with all its versions as it is and want to get a copy anyway you should choose the export/import option. This approach is actually supposed to export and import both versioned and unversioned vDisks from an existing store to a store in a different PVS farm. However you can leverage it to get a copy in one and the same vDisk store as follows:
- Copy the versioned vDisk including all *.vhd, *.ahvd, and *.pvp files. (Don’t copy *.lok files).
- Rename or label the copied files as required.
- Open the PVS console, right click the source vDisk, and choose ‘Export vDisk…’. A dialog appears.
- In the export dialog, choose the the latest version in the drop-down menu named ‘Export versions starting at’, then click OK. After a short delay the dialog closes. In the source vDisk’s store you’ll find a manifest file containing the entire information about the versions of that vDisk. The manifest file name matches with the name of the vDisk and it has a .xml suffix
- Rename the manifest file using the name of the copied vDisk.
- Open the manifest file in a text editor and accurately replace all references to the name of the source vDisk with the name of the copied vDisk. Double-check the changes, then save the file.
- Now, you should have a set of VHD/AVHD/PVP file and an XML file with the same base file name. And the manifest/XML refers to the new VHD and AVHD files.
- In the PVS console, right click the vDisk store, and choose ‘Add or Import Existing vDisk…’. A dialog appears.
- In the import dialog, check the settings for Site, Store, and Server, then click Search. After a short delay the copied vDisk will be displayed.
- Check the vDisk, check/uncheck the load balancing option as wanted, then click Add. After a short delay a popup appears saying that the import was successful. Click OK, then click Close in the import dialog.
Disclaimer: I hope that the information in this post is valuable to you. Your use of the information contained in this post, however, is at your sole risk. All information on this post is provided “as is”, without any warranty, whether express or implied, of its accuracy, completeness, fitness for a particular purpose, title or non-infringement, and none of the third-party products or information mentioned in the work are authored, recommended, supported or guaranteed by me. Further, I shall not be liable for any damages you may sustain by using this information, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such damages.
Identify and Remove Ghost NICs With devcon
With this post I share a small batch file that leverages the devcon utility from Microsoft to remove hidden network interfaces.
Recently Citrix identified an issue with those “Ghost NICs” when trying to image a target machine that resides on a virtualization platform like VMware (see CTX133188). NICs can be hidden of leftover from when the machine was built or when the virtualization tools were installed. If the PVS target software binds itself to one of these hidden NICs it won’t allow bnistack to load a vDisk properly.
Next question is how to get devcon because there’s no official download page. Check out this blog post
@ECHO OFF
devcon.exe findall =net >%TEMP%\%~n0-findall.tmp
devcon.exe find =net >%TEMP%\%~n0-find.tmp
FOR /F %%i IN ('find.exe "matching device" ^<%TEMP%\%~n0-findall.tmp') DO SET findall=%%i
FOR /F %%i IN ('find.exe "matching device" ^<%TEMP%\%~n0-find.tmp') DO SET find=%%i
IF %findall% EQU %find% (
ECHO Nothing to do.
GOTO :End
)
FOR /F "TOKENS=1,2 DELIMS=:" %%i IN ('find.exe /V "matching device" ^<%TEMP%\%~n0-findall.tmp') DO (
find.exe "%%i" <%TEMP%\%~n0-find.tmp >NUL || (
ECHO Remove "%%j"
devcon.exe remove "@%%i"
)
)
:End
DEL %TEMP%\%~n0-*.tmp
Disclaimer: I hope that the information in this post is valuable to you. Your use of the information contained in this post, however, is at your sole risk. All information on this post is provided “as is”, without any warranty, whether express or implied, of its accuracy, completeness, fitness for a particular purpose, title or non-infringement, and none of the third-party products or information mentioned in the work are authored, recommended, supported or guaranteed by me. Further, I shall not be liable for any damages you may sustain by using this information, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such damages.
[Updated] Prepare XenApp 6.x Server for Imaging with PVS
Hello again,
today I want to share a small PowerShell script that I use to semi-automatically prepare a XenApp 6.x server for imaging with PVS. It automates the following steps:
- Investigate the PVS’ Personality.ini in the root of the system drive in order to determine the disk mode (read-write, read-only, or started from local HD)
- Start the Citrix IMA service
- Query XenApp Server Load. Exit if the query fails. Exit if the XenApp server count is below 3.
- Run XenAppConfigConsole to prepare the server for imaging. (Based on the script user’s choice locally stored XenApp database information will be either kept or cleared from mf20.dsn and LGPO. Default is keep db information. If you choose to clear them you need to provide DB information through GPO.)
- Clear XenApp related caches (LHC and RADE)
- Clear Citrix User Profile Manager’s cache
- Resync time
- Update GPO settings
- Clear network related caches (DNS and ARP)
- Clear WSUS Client related settings
- Clear event logs
- Based on the findings in Step 1, suggest a convenient main action, that is either “Exit” (if we’re in maintenance/private w/ read-write vdisk access), or “Invoke ImagingWizard” (if we started from local HD), or “Invoke XenConvert” (reverse imaging scenario w/ read-only vdisk access)
# XenAppPrep.ps1
# --------------
# http://www.out-web.net/?p=1164
$DISK_MODE_READ_WRITE = 0
$DISK_MODE_LOCAL_HD = 1
$DISK_MODE_READ_ONLY = 2
$ANSWER_YES = 0
$ANSWER_NO = 1
function Read-Choice
{
param (
$Caption,
$Message,
$Option = $(throw 'Please specify some options.'),
$HelpText,
$Default = 0
)
[Management.Automation.Host.ChoiceDescription[]] $Choices = @()
for ($i = 0; $i -lt $Option.Length; $i++)
{
$OptionText = $Option[$i]
$Choice = New-Object Management.Automation.Host.ChoiceDescription $OptionText
if($HelpText -and $HelpText[$i])
{
$Choice.HelpMessage = $HelpText[$i]
}
$Choices += $Choice
}
$host.UI.PromptForChoice($Caption, $Message, $Choices, $Default)
}
# ------------------------------------------------------------------------------
Write-Host 'Provisioning Server Image Prepare Script for XenApp 6.x' -ForegroundColor White
$Choice = Read-Choice -Caption 'PVS XenApp Image Prep' `
-Message 'Are you ready to start?' `
-Option '&Yes','&No' -HelpText 'Yes, execute this script','No, exit this script' `
-Default 1
if ($Choice -eq $ANSWER_NO)
{
return
}
# ------------------------------------------------------------------------------
if (Test-Path -Path ${env:SystemDrive}\Personality.ini)
{
[string] $DiskName = & "${env:ProgramFiles}\Citrix\Provisioning Services\GetPersonality.exe" `$DiskName /o
Write-Host "Disk Name: $DiskName"
[int] $WriteCacheType = & "${env:ProgramFiles}\Citrix\Provisioning Services\GetPersonality.exe" `$WriteCacheType /o
switch ($WriteCacheType)
{
0
{
$Mode = $DISK_MODE_READ_WRITE
Write-Host "Disk Mode: Private/Maintenance (Read-Write)"
}
default
{
$Mode = $DISK_MODE_READ_ONLY
Write-Host "Disk Mode: Standard (Read-Only)"
}
}
}
else
{
$DiskName = ''
$Mode = $DISK_MODE_LOCAL_HD
Write-Host "Disk Mode: Local Hard Disk"
}
# ------------------------------------------------------------------------------
Write-Host '1. Checking IMA-Server Status (must be Running)'
$IMAservice = Get-Service -Name imaservice -ErrorAction SilentlyContinue
if ($IMAservice.Status -eq 'Stopped')
{
Write-Host 'Start Citrix IMA service'
$IMAservice.Start()
$IMAservice.WaitForStatus('Running')
}
# ------------------------------------------------------------------------------
Write-Host '2. Import XenApp 6.5 PowerShell SnapIns. This may take a while.'
Add-PSSnapIn Citrix.XenApp.Commands -ErrorAction SilentlyContinue
# ------------------------------------------------------------------------------
Write-Host '3. Query Farm for correct IMA Communication'
$Qfarm_Status = Get-XAServerLoad -ErrorAction SilentlyContinue
if ($Qfarm_Status.Count -gt 2)
{
Write-Host 'Query Farm succesfully finished.' -ForegroundColor DarkGreen
}
else
{
Write-Host 'Error querying XenApp Farm' -ForegroundColor Red
Write-Host $Qfarm_Status
Write-Host "Script will be closed in less than 15 seconds. Check XenApp Status for running Script again." -ForegroundColor Red
Start-Sleep -Seconds 15
return
}
# ------------------------------------------------------------------------------
$Choice = Read-Choice -Caption '4. Prepare XenApp for imaging' `
-Message 'Clear database information (name and server) from mf20.dsn and LGPO?' `
-Option '&Yes','&No' -HelpText 'Yes, I provide database and zone information through GPO','No, please keep mf20.dsn' `
-Default 1
if ($Choice -eq $ANSWER_NO)
{
& "${env:ProgramFiles(x86)}\Citrix\XenApp\ServerConfig\XenAppConfigConsole.exe" /ExecutionMode:ImagePrep /RemoveCurrentServer:True /PrepMsmq:True
}
else
{
& "${env:ProgramFiles(x86)}\Citrix\XenApp\ServerConfig\XenAppConfigConsole.exe" /ExecutionMode:ImagePrep /RemoveCurrentServer:True /PrepMsmq:True /ClearLocalDatabaseInformation:True
}
if ($LASTEXITCODE -gt 0)
{
Write-Host 'Error preparing XenApp server' -ForegroundColor Red
Write-Host "Script will be closed in less than 15 seconds. Check XenApp Status for running Script again." -ForegroundColor Red
Start-Sleep -Seconds 15
return
}
# ------------------------------------------------------------------------------
Write-Host '5. Clear Citrix related caches (LHC, RADE, UPM)'
DsMaint.exe RECREATELHC
DsMaint.exe RECREATERADE
$UPMCACHE = "${env:ProgramFiles}\Citrix\User Profile Manager\UserProfileManager_?.cache"
if (Test-Path -Path $UPMCACHE)
{
$UPMservice = Get-Service -Name ctxProfile -ErrorAction SilentlyContinue
if ($UPMservice.Status -eq 'Running')
{
$IMAservice.Stop()
$IMAservice.WaitForStatus('Stopped')
}
Remove-Item $UPMCACHE -Force
}
# ------------------------------------------------------------------------------
Write-Host '6. Resync time'
Write-Host 'Checking W32Time-Service Status (must be Running)'
$TIMEservice = Get-Service -Name W32Time -ErrorAction SilentlyContinue
if ($TIMEservice.Status -eq 'Stopped')
{
Write-Host 'Start W32Time service'
$TIMEservice.Start()
$TIMEservice.WaitForStatus('Running')
}
w32tm.exe /config /update
w32tm.exe /resync
# ------------------------------------------------------------------------------
Write-Host '7. Update GPO settings'
gpupdate.exe /force
# ------------------------------------------------------------------------------
Write-Host '8. Clear networking related caches (DNS and ARP)'
ipconfig.exe /flushdns
arp.exe -d
# ------------------------------------------------------------------------------
Write-Host '9. Clear WSUS Client related regisrty keys'
$WUservice = Get-Service -Name wuauserv -ErrorAction SilentlyContinue
if ($WUservice.Status -eq 'Running')
{
Write-Host 'Stop Windows Update service'
$WUservice.Stop()
$WUservice.WaitForStatus('Stopped')
}
$WUregkey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate'
Remove-ItemProperty -Path $WUregkey -Name AccountDomainSid -ErrorAction SilentlyContinue
Remove-ItemProperty -Path $WUregkey -Name SusClientId -ErrorAction SilentlyContinue
Remove-ItemProperty -Path $WUregkey -Name PingID -ErrorAction SilentlyContinue
Remove-ItemProperty -Path $WUregkey -Name SusClientIdValidation -ErrorAction SilentlyContinue
# ------------------------------------------------------------------------------
Write-Host '10. Clear event logs'
Get-EventLog -List | ForEach-Object {Clear-EventLog -LogName $_.Log} -ErrorAction Inquire
# ------------------------------------------------------------------------------
$Choice = Read-Choice -Caption '11. PVS XenApp Image Prep' `
-Message 'Choose between the following options' `
-Option '&Exit', '&ImagingWizard', '&XenConvert' `
-HelpText 'Exit the Script to shutdown this machine manually (Private/Maintenance vDisk mode)', `
'Invoke PVS ImagingWizard to create a new VHD from this machine (vP2PVS)', `
'Invoke XenConvert to image this machine to existing and already mapped vDisk or make a re-image (PVS2vP, vP2PVS)' `
-Default $Mode
switch ($Choice)
{
0
{
Write-Host 'You chose "Exit". Finishing script with TargetDeviceOptimizer...' -ForegroundColor DarkGreen
Start-Process "${env:ProgramFiles}\Citrix\Provisioning Services\TargetOSOptimizer.exe"
}
1
{
Write-Host 'You chose "ImagingWizard". Invoking PVS ImagingWizard...' -ForegroundColor DarkGreen
Start-Process "${env:ProgramFiles}\Citrix\Provisioning Services\ImagingWizard.exe" -Wait
}
2
{
Write-Host 'You chose "XenConvert". Invoking Citrix XenConvert...' -ForegroundColor DarkGreen
Start-Process "${env:ProgramFiles}\Citrix\XenConvert\XenConvert.exe" -Wait
}
}
# ------------------------------------------------------------------------------
Write-Host 'Script finished. Press any key to exit.' -ForegroundColor Green
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Write-Host 'KKTHXBYE' -ForegroundColor DarkGreen
The script raises no claim to completeness. For example, you should consider running chkdsk C: as consistency check and Mark Russinovich’s SDelete to reduce storage needs.
Disclaimer: I hope that the information in this post is valuable to you. Your use of the information contained in this post, however, is at your sole risk. All information on this post is provided “as is”, without any warranty, whether express or implied, of its accuracy, completeness, fitness for a particular purpose, title or non-infringement, and none of the third-party products or information mentioned in the work are authored, recommended, supported or guaranteed by me. Further, I shall not be liable for any damages you may sustain by using this information, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such damages.
















