App-V 5 Configuration Editor (ACE) v1.4 Released!

ACE-256px_thumb.pngVirtual Engine are pleased to announce the version 1.4 release of the App-V 5 Configuration Editor (ACE). This (free) utility provides a simple user interface for editing App-V 5 dynamic configuration and manifest files, without manually hacking the underlying XML files.

This release requires Microsoft .NET Framework 4.5 or above and contains several reported bug fixes along with:

  • Support for App-V 5.1 (See excellent blog post by @ThamimKarim for a run down of what’s new in App-V 5.1);
  • Ability to modify AppxManifest files to support the App-V 5.1 sequencer Export/Import feature;
  • New Applications tab has been added, where you can Enable or Disable applications when published;
  • Improved Error checking for missing data;
  • New setting added to the User and Machine scripts that enables scripts to Run Asynchronously;
  • Tool tips added to explain what various settings or options will do;
  • New options added to the Shortcuts Context Menu that make it even easier to add new shortcuts;
  • Ability to pop-out the Generated XML and Source XML into a separate viewable window;
  • Comments have been added in the Generated XML file to make it easier to read each section;
  • Drag and Drop support has been added to open the dynamic configuration files;
  • Ability to save the Generated XML for use in App-V Scheduler.

Mocking Missing Cmdlet ErrorAction with Pester

Following on from the previous Mocking Missing Cmdlets with Pester post, I also encountered another interesting problem when attempting to mock cmdlets that were not present on the test system. This one is more of an edge-case, hence its own post. Just like last time, the tests worked when I had the Hyper-V cmdlets installed, but failed when running within an Appveyor VM.

It’s probably not uncommon that you will need to ensure that the code under test should throw an error here-and-there. Here is a pseudo-example that tests that Get-VM writes an error when passed with a non-existent VM name:

Describe 'Mocking ErrorAction Example' {
    Function Get-VM { param ($Name) }
    InModuleScope 'xVMHyper-V' {

        It 'Get-VM Throws' {
            Mock Get-VM -ParameterFilter { $Name -eq 'TestVM' } -MockWith { Write-Error 'Oops' }
            { Get-VM -Name 'TestVM' -ErrorAction Stop } | Should Throw;
        }
    }
}

When this test is run it fails:

Describing Mocking ErrorAction Example
 Write-Error 'Oops'  : Oops
At C:\Program Files\WindowsPowerShell\Modules\Pester\Functions\Mock.ps1:709 char:21
+                     & $___ScriptBlock___ @___BoundParameters___ @___ArgumentList ...
+                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException 
 [-] Get-VM Throws 176ms
   Expected: the expression to throw an exception
   at line: 6 in D:\Users\Iain\Desktop\PesterDemo.Tests.ps1
Tests completed in 176ms
Passed: 0 Failed: 1 Skipped: 0 Pending: 0

The problem here is that the stub function is not an advanced function and the –ErrorAction preference switch is ignored! This is easily resolved by adding the [CmdletBinding()] attribute to the stub function definition:

Describe 'Mocking ErrorAction Example' {
    Function Get-VM { [CmdletBinding()] param ($Name) }
    InModuleScope 'xVMHyper-V' {

        It 'Get-VM Throws' {
            Mock Get-VM -ParameterFilter { $Name -eq 'TestVM' } -MockWith { Write-Error 'Oops' }
            { Get-VM -Name 'TestVM' -ErrorAction Stop } | Should Throw;
        }
    }
}

Running the test now results in the expected output:

Describing Mocking ErrorAction Example
 [+] Get-VM Throws 177ms
Tests completed in 177ms
Passed: 1 Failed: 0 Skipped: 0 Pending: 0

This is not a Pester issue and it’s not a Powershell issue either. It’s just the way the normal Powershell functions work. But, just in case someone else runs into it I thought it would be worth quickly documenting!

Mocking Missing Cmdlets with Pester

When writing Pester unit tests for your Powershell code you will probably have a need to mock calls to external functions before too long. This process works as you would expect when Pester can locate a defined function/cmdlet with a matching name. However, if Pester cannot find a definition, it will fail.

This problem will normally surface in a Continuous Integration (CI) environment. For me, it was writing the first suite of tests for the xHyper-V DSC resource module. The Hyper-V cmdlets where present on my authoring machine but were not present on the Appveyor build VM.

Here is an overly simplified Pester test that I’ll use for demonstration purposes:

Describe 'Missing Cmdlet Mocking Example' {
    InModuleScope 'xVMHyper-V' {

        It 'Calls Get-VM' {
            Mock Get-VM -MockWith { }
            Get-VM -Name 'TestVM';
            Assert-MockCalled Get-VM -Scope It;
        }

    }
}

If we run the test and Pester cannot locate a defined function then it will report an error. Note: if you run this on a machine with the Hyper-V module installed (and you have a VM called ‘TestVM’) then it will pass – but you knew that already ;).

Describing Missing Cmdlet Mocking Example
 [-] calls Get-VM 474ms
   Could not find Command Get-VM
   at line: 600 in C:\Program Files\WindowsPowerShell\Modules\Pester\Functions\Mock.ps1
Tests completed in 474ms

This is easily overcome by defining an empty function within the test file. Note: this will need to be defined within the ‘InModuleScope’ script block if you’re testing a module’s internals.

Describe 'Missing Cmdlet Mocking Example' {
    InModuleScope 'xVMHyper-V' {

        Function Get-VM { }

        It 'Calls Get-VM' {
            Mock Get-VM -MockWith { }
            Get-VM -Name 'TestVM';
            Assert-MockCalled Get-VM -Scope It;
        }

    }
}

The test now passes as we would expect. Yay \o/

Describing Missing Cmdlet Mocking Example
 [+] Calls Get-VM 141ms
Tests completed in 141ms
Passed: 1 Failed: 0 Skipped: 0 Pending: 0

Now, what you really need to know is that for Pester to enumerate and mock parameter filters, those parameters need to be defined on the stub function. If we were to update the test to check for the passing of a particular –Name parameter like so:

Describe 'Missing Cmdlet Mocking Example' {
    InModuleScope 'xVMHyper-V' {

        Function Get-VM { }

        It 'Calls Get-VM' {
            Mock Get-VM –ParameterFilter { $Name –eq ‘TestVM } -MockWith { }
            Get-VM -Name 'TestVM';
            Assert-MockCalled Get-VM –ParameterFilter { $Name –eq ‘TestVM’ } -Scope It;
        }

    }
}

When we run the test it will now fail again.

Describing Missing Cmdlet Mocking Example
 [-] Calls Get-VM 131ms
   Expected Get-VM to be called at least 1 times but was called 0 times
   at line: 518 in C:\Program Files\WindowsPowerShell\Modules\Pester\Functions\Mock.ps1
Tests completed in 131ms
Passed: 0 Failed: 1 Skipped: 0 Pending: 0

For Pester to enumerate the dynamic parameters on the function, it needs to have the parameters (only the one’s you’re interested in) defined. This can easily be fixed like so:

Describe 'Missing Cmdlet Mocking Example' {
    InModuleScope 'xVMHyper-V' {

        Function Get-VM { param ($Name) }

        It 'Calls Get-VM' {
            Mock Get-VM –ParameterFilter { $Name –eq ‘TestVM } -MockWith { }
            Get-VM -Name 'TestVM';
            Assert-MockCalled Get-VM –ParameterFilter { $Name –eq ‘TestVM’ } -Scope It;
        }

    }
}

The tests will now once again pass successfully!

Describing Missing Cmdlet Mocking Example
 [+] Calls Get-VM 169ms
Tests completed in 169ms
Passed: 1 Failed: 0 Skipped: 0 Pending: 0

Hopefully this helps someone and saves some time. It took me a while to work out what was going on as I had the cmdlets available on my development machine but the tests were failing when running in an Appveyor VM. Perhaps I should submit a pull request to get this put into the Pester help documentation?!

Let’s Get Chocolatey

It’s taken a little while, but we are pleased to announce that the Virtual Engine Toolkit (VET) and the App-V Configuration Editor (ACE) are now available on Chocolatey! If you have Chocolatey installed on your systems then you can get going by running the commands listed below.

image

Microsoft Package Management

It gets even better if you’re running the Windows 10 Insider preview, the latest WMF 5.0 April 2015 preview or have installed the latest experimental OneGet build on Windows 7 and up, you can download these directly – today!

To do this, run the Install-Package ace or Install-Package vet commands, like so:

image
Other Packages

It doesn’t end here though. We have also published the following packages that you might find useful to the public Chocolatey feed:

Now – go get Chocolatey 😀

Testing Private Functions with Pester

We’ve been busy beavering away on a new Powershell module that is comprised of many .ps1 files, that are loaded by a master/control .psm1 file when the module is imported. And, like all good Powershell citizens, we have many Pester unit tests for this code in accompanying .Tests.ps1 files.

As Powershell script modules permit us to control which functions should be exported with Export-ModuleMember and Pester allows us to test non-exported functions with the InModuleScope option, you might be wondering why we would ever need to be able to test private/internal functions?

Script Bundles

Whilst coding the new Powershell module, we have always had the desire to ensure that it could also be used as a ‘bundled’ .ps1 file. By bundle, we mean a single combined .ps1 file that can be included verbatim at the beginning of an existing script or by dot-sourcing it as required. Unfortunately – in this particular scenario – the internal module functions would be exposed and could potentially cause unnecessary confusion.

Here’s an example where both the ‘PublicFunction’ and ‘InnerPrivate’ functions would be exposed when bundled or dot-sourced into an existing .ps1 file.

function InnerPrivate {
    param ()
    Write-Output ‘InnerPrivate’
}

function PublicFunction {
    param()
    Write-Output (InnerPrivate)
}

If we only want the ‘PublicFunction’ visible then the simple solution to this is to nest the private function(s) inside the public function(s) like so:

function PublicFunction {
    param()

    function InnerPrivate {
        param ()
        Write-Output ‘InnerPrivate’
    }

   Write-Output (InnerPrivate)
}

Now only the ‘PublicFunction’ will be available. End of the story?

Internal Functions

Not quite; by hiding the functions we now cannot test them with Pester. I had a very brief conversation with Dave Wyatt on GitHub about this and it was agreed that this functionality should not be a part of the official Pester release.

To solve this particular issue, we have a simple function that will locate and return a function’s definition from within a .ps1 file as a script block. The function definition can then be dot-sourced into the current Pester scope to enable testing. Here’s an example Pester test file that will test our ‘InnerPrivate’ function:

$here = Split-Path –Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path –Leaf $MyInvocation.MyCommand.Path).Replace(“.Tests.”, “.”)
# If only testing an internal function, there is no need to dot-source the entire contents..

Describe “InnerPrivate” {
    # Import the ‘InnerPrivate’ function into the current scope
    . (Get-FunctionDefinition –Path “$here\$sut” –Function InnerPrivate)

    It “tests the private, inner function” {
        InnerPrivate | Should Be ‘InnerPrivate’
    }
}

If this code is of interest you can simply save the following code as a .ps1 file in Pester’s \Function directory, for example \Functions\FunctionDefinition.ps1, and Pester will automatically load it.

Notes:

  1. As this code utilises the Abstract Syntax Tree (AST) it does require Powershell 3.0;
  2. If there are any dependencies on variables in the function’s parent scope these will need to be mocked/accounted for;
  3. Depending on how you install/update the Pester module, this might get overwritten when Pester is updated.

When we have more time we’ll put this up on Github so people can collaborate on changes. In the meantime, here’s a copy of the function.

#Requires -Version 3

<#
.SYNOPSIS
    Retrieves a function's definition from a .ps1 file or ScriptBlock.
.DESCRIPTION
    Returns a function's source definition as a Powershell ScriptBlock from an
    external .ps1 file or existing ScriptBlock. This module is primarily
    intended to be used to test private/nested/internal functions with Pester
    by dot-sourcsing the internal function into Pester's scope.
.PARAMETER Function
    The source function's name to return as a [ScriptBlock].
.PARAMETER Path
    Path to a Powershell script file that contains the source function's
    definition.
.PARAMETER LiteralPath
    Literal path to a Powershell script file that contains the source
    function's definition.
.PARAMETER ScriptBlock
    A Powershell [ScriptBlock] that contains the function's definition.
.EXAMPLE
    If the following functions are defined in a file named 'PrivateFunction.ps1'

    function PublicFunction {
        param ()

        function PrivateFunction {
            param ()
            Write-Output 'InnerPrivate'
        }

        Write-Output (PrivateFunction)
    }

    The 'PrivateFunction' function can be tested with Pester by dot-sourcing
    the required function in the either the 'Describe', 'Context' or 'It'
    scopes.

    Describe "PrivateFunction" {
        It "tests private function" {
            ## Import the 'PrivateFunction' definition into the current scope.
            . (Get-FunctionDefinition -Path "$here\$sut" -Function PrivateFunction)
            PrivateFunction | Should BeExactly 'InnerPrivate'
        }
    }
.LINK
    
Testing Private Functions with Pester
#> function Get-FunctionDefinition { [CmdletBinding(DefaultParameterSetName='Path')] [OutputType([System.Management.Automation.ScriptBlock])] param ( [Parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName='Path')] [ValidateNotNullOrEmpty()] [Alias('PSPath','FullName')] [System.String] $Path = (Get-Location -PSProvider FileSystem), [Parameter(Position = 0, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'LiteralPath')] [ValidateNotNullOrEmpty()] [System.String] $LiteralPath, [Parameter(Position = 0, ValueFromPipeline = $true, ParameterSetName = 'ScriptBlock')] [ValidateNotNullOrEmpty()] [System.Management.Automation.ScriptBlock] $ScriptBlock, [Parameter(Mandatory = $true, Position =1, ValueFromPipelineByPropertyName = $true)] [Alias('Name')] [System.String] $Function ) begin { if ($PSCmdlet.ParameterSetName -eq 'Path') { $Path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path); } elseif ($PSCmdlet.ParameterSetName -eq 'LiteralPath') { ## Set $Path reference to the literal path(s) $Path = $LiteralPath; } } # end begin process { $errors = @(); $tokens = @(); if ($PSCmdlet.ParameterSetName -eq 'ScriptBlock') { $ast = [System.Management.Automation.Language.Parser]::ParseInput($ScriptBlock.ToString(), [ref] $tokens, [ref] $errors); } else { $ast = [System.Management.Automation.Language.Parser]::ParseFile($Path, [ref] $tokens, [ref] $errors); } [System.Boolean] $isFunctionFound = $false; $functions = $ast.FindAll({ $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true); foreach ($f in $functions) { if ($f.Name -eq $Function) { Write-Output ([System.Management.Automation.ScriptBlock]::Create($f.Extent.Text)); $isFunctionFound = $true; } } # end foreach function if (-not $isFunctionFound) { if ($PSCmdlet.ParameterSetName -eq 'ScriptBlock') { $errorMessage = 'Function "{0}" not defined in script block.' -f $Function; } else { $errorMessage = 'Function "{0}" not defined in "{1}".' -f $Function, $Path; } Write-Error -Message $errorMessage; } } # end process } #end function Get-Function

App-V 5 Sequencer Template – Full VFS Write Mode

With the recent release of Hotfix 4 for App-V 5.0, Microsoft has now provided the ability to “Allow virtual applications full write permissions to the virtual file system”. This setting can be found in the sequencer under the “Advanced” tab as demonstrated in the screen shot below:

 Sequncer Full VFS

Should you wish to enable this setting as a default whenever you create a new package, then simply go ahead and add <FullVFSWriteMode>true<FullVFSWriteMode> into your sequencer template (.appvt), as you can see below. This setting is only valid where you have the App-V 5.0 SP2 Hotfix 4 sequencer installed.

<?xml version="1.0" encoding="utf-8"?>
<SequencerTemplate xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <AllowMU>false</AllowMU>
  <AppendPackageVersionToFilename>false</AppendPackageVersionToFilename>
  <AllowLocalInteractionToCom>false</AllowLocalInteractionToCom>
  <AllowLocalInteractionToObject>false</AllowLocalInteractionToObject>
  <FullVFSWriteMode>true</FullVFSWriteMode>
  <ExcludePreExistingSxSAndVC>false</ExcludePreExistingSxSAndVC>
  <FileExclusions>
    <string>[{CryptoKeys}]</string>
    <string>[{Common AppData}]\Microsoft\Crypto</string>
    <string>[{Common AppData}]\Microsoft\Search\Data</string>
    <string>[{Cookies}]</string>
    <string>[{History}]</string>
    <string>[{Cache}]</string>
    <string>[{Local AppData}]</string>
    <string>[{Personal}]</string>
    <string>[{Profile}]\Local Settings</string>
    <string>[{Profile}]\NTUSER.DAT.LOG1</string>
    <string>[{Profile}]\NTUSER.DAT.LOG2</string>
    <string>[{Recent}]</string>
    <string>[{Windows}]\Debug</string>
    <string>[{Windows}]\Logs\CBS</string>
    <string>[{Windows}]\Temp</string>
    <string>[{Windows}]\WinSxS\ManifestCache</string>
    <string>[{Windows}]\WindowsUpdate.log</string>
    <string>[{AppVPackageDrive}]\$Recycle.Bin</string>
    <string>[{AppVPackageDrive}]\System Volume Information</string>
    <string>[{AppData}]\Microsoft\AppV</string>
    <string>[{Local AppData}]\Temp</string>
    <string>[{ProgramFilesX64}]\Microsoft Application Virtualization\Sequencer</string>
  </FileExclusions>
  <RegExclusions>
    <string>REGISTRY\MACHINE\SOFTWARE\Wow6432Node\Microsoft\Cryptography</string>
    <string>REGISTRY\MACHINE\SOFTWARE\Microsoft\Cryptography</string>
    <string>REGISTRY\USER\[{AppVCurrentUserSID}]\Software\Microsoft\Windows\CurrentVersion\Explorer\StreamMRU</string>
    <string>REGISTRY\USER\[{AppVCurrentUserSID}]\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\StreamMRU</string>
    <string>REGISTRY\USER\[{AppVCurrentUserSID}]\Software\Microsoft\Windows\CurrentVersion\Explorer\Streams</string>
    <string>REGISTRY\USER\[{AppVCurrentUserSID}]\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\Streams</string>
    <string>REGISTRY\MACHINE\SOFTWARE\Microsoft\AppV</string>
    <string>REGISTRY\MACHINE\SOFTWARE\Wow6432Node\Microsoft\AppV</string>
    <string>REGISTRY\USER\[{AppVCurrentUserSID}]\Software\Microsoft\AppV</string>
    <string>REGISTRY\USER\[{AppVCurrentUserSID}]\Software\Wow6432Node\Microsoft\AppV</string>
  </RegExclusions>
  <TargetOSes />
</SequencerTemplate>

And if you’re not currently using a template then I’d highly recommend you do. You’ll find a great article over at Rory Monaghan’s blog that explains the use of the sequencer template in more detail.

Nathan

Windows 8.1 Update KB 2919355 Error 0x8007003

It’s been long, too long since the last post. However, never fear as we have plenty of new updates in the pipeline (pun intended!). This post is a little off topic but one that might help one or two people with the latest Windows 8.1 Update 1.

Whilst installing the latest Windows 8.1 Update 1 (and also KB2894853) on a couple of machines I saw the following error:

Installation Failure: Windows failed to install the following update with error 0x8007003: Update for Windows (KB2919355)

A quick Google landed me here which mentions that you will receive this error if the profiles directory is redirected with a registry key. As we have small SSDs in the office machines, the profile directory is indeed redirected to a bigger, spinning disk. This article is a little confusing (hence the post) as it states:

After putting a copy of UserProfiles on the C: drive, installation finished normally.

To fix the installation error we need to ensure that the Default directory is present in the redirected folder location, not on the C: drive. A quick copy of C:\Users\Default to D:\Users\Default fixed the installation issues with both updates.

VET v1.4 Released!

Virtual Engine are pleased to announce the general availability of version 1.4 of the Virtual Engine Toolkit (VET). The latest Windows installer and documentation is available for download now on the Virtual Engine web site.

There’s no “What’s New” video this time but to view existing videos on the Virtual Engine Toolkit, please check out our YouTube channel.

App-V 5 Configuration Editor User Guide

ACEWe’ve been working hard getting the App-V 5 Configuration Editor (ACE) ready for official release; take a look at the ACE page for a bit more information about why it was developed.

The purpose of this short blog to guide you through the ACE interface. There is an assumption here you have an understanding of the App-V 5 Dynamic Configuration files and how they are used, if not you might want to take a look at this Technet article.

USER INTERFACE

Main Toolbar:

You will notice there are three main buttons in the tool bar as shown below:

Main Toolbar

image Opens an App-V XML file, i.e. a UserConfig.xml or DeploymentConfig.xml file. Once the file has been opened the contents will be parsed and displayed under the various tabs within the GUI.

image Saves the current App-V XML file, including any changes that have been made. You can give it a new name and Save As a different file, keeping your original one as is if necessary.

image Previews the changes that will be made to the App-V XML file before saving. This gives you the ability to check out the structure of the generated XML. It’s probably a good idea to point out here that you don’t need to preview the changes prior to performing a save.

Package Details:

This sections displays the Package Display Name, Package ID and Type of XML file opened, i.e. DeploymentConfig or UserConfig. Here is an example DeploymentConfig.xml opened below:

Package Details

MAIN CONFIGURATION TABS

Once an App-V 5 configuration XML file has been opened you can then begin to make changes as required using the tabs set out below.

User Configuration

Under the User Configuration tab you can change and view various options and configurations:

User Configuration

Options

Various global options change be changed here if you so desire, e.g. altering the COM integration mode.

Shortcuts

This tab allows you to View, Add, Edit or Delete any Shortcuts within the package.

If you want to delete an existing shortcut, simply select the row that contains the shortcut and press delete. Should you wish to add a new shortcut, I’d suggest you copy and paste an existing row and then edit the fields accordingly; we’ve added a context menu to make that task easy, should you not fancy using Ctrl+C and Ctrl+V  Smile.

Shortcuts

Scripts (User Context)

This is really where ACE starts to make life simple Smile. You can easily define which scripts you’d like to add and to which script actions, e.g. PublishPackage, UnpublishPackage, StartVirtualEnvironment, TerminateVirtualEnvironment, StartProcess and ExitProcess. There is no need to worry about getting the syntax in the XML file right. There are some excellent blogs out there talking about using scripts in App-V 5.0, so I suggest you take a look here at one from Tim Mangan and Microsoft’s own Steve Thompson if you need some further background information.

NOTE: You might have noticed that not all the script actions are available under this tab, that’s simply because those excluded aren’t permitted to run under the User Configuration section of the XML file.

I think most of the options are self explanatory but, it’s good to point out that leaving the Timeout value at 0 means no timeout period will be set, i.e. it will wait indefinitely for it to finish so use with caution.

Scripts (User Context)

Machine Configuration

Under the Machine Configuration tab you can alter global options, configure scripts and control the termination of processes.

NOTE: this tab will only be available when you open a DeploymentConfig.xml file. This is because machine configuration items cannot be set in the UserConfig.xml file.

Machine Configuration

Options

Here you’ll find any options that can be changed if you so desire.

Terminate Child Processes

You can define the path to an executable, that when closed, will terminate any child process running within the virtual environment.

Terminate Child Processes

Scripts (System Context)

Very much like the Scripts tab under User Configuration you can define which scripts you’d like to add to which machine script actions, e.g. AddPackage, RemovePackage, PublishPackage and UnpublishPackage.

NOTE: You might have noticed that not all the script actions are available under this tab, that’s simply because those excluded aren’t permitted to run under the Machine Configuration section of the XML file.

Scripts (System Context)

XML

You can view both the source (original) XML and/or preview the generated XML under this tab.

Source XML

Source XML

This is simply where you can view your source App-V XML file as it was when you opened it.

Generated XML

Once you click the Preview button image this pane will display any changes that will be made to the App-V XML file, giving you the ability to check out the structure of the XML before saving if you wish. NOTE: You don’t have to preview the changes prior to performing a save.

The example below (highlighted in yellow) shows the changes made by ACE in the generated XML format. NOTE: ACE will not highlight the changes in the XML, we’ve done it here for clarity purposes only.

Generated XML

With any luck this brief guide has given you a good overview of how to use ACE and hopefully you’ll agree its pretty intuitive to use and should make editing the App-V 5 Dynamic Configuration files a lot, lot easier (well we think so anyway!)? 🙂

DISCLAIMER: THE APP-V CONFIGURATION EDITOR IS FREE TO USE AT YOUR OWN RISK, WE CANNOT BE HELD RESPONSIBLE FOR ANY DAMAGE IT MIGHT CAUSE.