Pages

Monday, June 21, 2021

Planning for an EA to CSP Azure Subscription to Subscription Migration

One of the projects I've been recently involved in is a EA to CSP subscriptions migration for a client and those who have been a part of such a project will know that there are two methods for the migration. One is a manual subscription to subscription migration, which requires quite a bit of planning before execution, and the other is a billing transfer that is available to Partners who are Azure Expert MSPs. I've been fortunate enough to have access to the Azure Expert MSPs method (https://docs.microsoft.com/en-us/azure/cost-management-billing/manage/mpa-request-ownership#request-billing-ownership), which eliminates majority of the heavy lifting. With that said, I did have to perform a manual migration a year ago so I wanted to share the assessment, planning and migration tips I have in hopes of providing help to anyone have to go through this exercise.

Useful documentation, blog posts and forums for subscription migration information

Let me begin by providing the following list of links that I found very useful when planning for a subscription to subscription migration through the use moving resources between EA to CSP and Pay-as-you-go to CSP.

Microsoft Documentation:

Move resources to a new resource group or subscription - most important to documentation to read to start the migration planning:
https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/move-resource-group-and-subscription

Move operation support for resources - use this to determine whether a resource can be migrated between subscriptions by obtaining its type and reviewing the information provided by the table (e.g. Microsoft.ClassicCompute/domainnames):
https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/move-support-resources

How to move a Recovery Services vault across Azure Subscriptions and Resource Groups:
https://docs.microsoft.com/en-gb/azure/backup/backup-azure-move-recovery-services-vault

Move guidance for virtual machines:
https://docs.microsoft.com/en-gb/azure/azure-resource-manager/management/move-limitations/virtual-machines-move-limitations

Blogs and GitHub:

A must read blog post by Jack Tracey, a Microsoft Cloud Solutions Architect, that provides many important details about manually migrating resources as well as the Azure Expert MSP billing transfer:
https://jacktracey.co.uk/migration/azure-subscription-migrations/

Igor Shastitko wrote a LinkedIn post outlining his experience, items to lookout for, and a high level plan:
https://www.linkedin.com/pulse/azure-paygea-csp-subscriptions-migrations-limitations-igor-shastitko/

Morten Pedholt provides his experience, migration planning tips and how to call the validation API to determine whether resources can be migrated from one subscription to another:
https://pedholtlab.com/migrate-between-azure-subscriptions-like-a-pro/

Pantelis Apostolidis provides a way to use Postman to run the resource migration validation:
https://www.cloudcorner.gr/microsoft/azure/validate-azure-resource-move-with-postman/

Jeroen shares his experience with subscription to subscription migration:
http://www.jeroenwint.com/2017/08/09/lessons-learned-migrate-between-azure-ea-and-azure-csp-subscription/

How to migrate Azure IaaS VMs if they are not encrypted with SSE with PMK with Azure Key Vault:
https://github.com/MicrosoftDocs/azure-docs/issues/8684#issuecomment-400024929

https://stackoverflow.com/questions/55922310/can-we-move-encrypted-virtual-machines-from-subscription-to-subscription

How to migrate application gateways:
https://github.com/MicrosoftDocs/azure-docs/issues/8037

PowerShell script to validate whether a resource can be moved across subscriptions:
https://www.powershellbros.com/check-possibility-of-azure-resource-migration/

Azure Resource Mover for moving resources:
https://www.red-gate.com/simple-talk/cloud/infrastructure-as-a-service/lets-move-azure-resource-mover/

While not related to migrating resources from one subscriptions to another, Wesley Haakman outlines the process of transfer the billing ownership of an Azure subscription from an Enterprise Agreement to a CSP:
https://www.wesleyhaakman.org/transferring-ea-subscriptions-to-csp/

Export subscription resources and review items for migration

Begin by downloading Jack Tracey's Azure Resource Migration Support Tool spreadsheet from his blog: AzureResourceMigrationSupportV10.xlsx

https://jacktracey.co.uk/migration/azure-subscription-migrations/

The Introduction tab of the spreadsheet will provide a detailed explanation of the spreadsheet's features and functionality. Before proceeding to populate the spreadsheet with data, it would be best to update it because the spreadsheet available on Jack Tracey's blog was last updated on October 2020 and Microsoft continues to make new improvements to the subscription migration feature so he has provided links to github where CSVs containing an updated list of whether resources can be migrated. Proceed to download the latest move-support-resources.csv CSV file:

image

Then copy it into the move-support-resources tab in the AzureResourceMigrationSupportV10.xlsx spreadsheet:

image

With the spreadsheet updated, proceed to the Introduction tab, which will contain the following PowerShell cmdlet to export the Name, ResourceGroupName, Location and Type of every resource in the subscription into a CSV file:

Connect-AzAccount

Get-AzSubscription -SubscriptionId xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx | Set-AzContext

Get-AzResource | Select Name,ResourceGroupName,Location,Type | Export-CSV SubscriptionXXXXExport.csv -NoTypeInformation

image

Another method of obtaining the resources export is to use Azure Resource Graph Explorer with the following query:

resources
| project name, resourceGroup, location, type
| order by type asc

image

With the CSV file of the resources created, paste the resources into the first 4 columns (Name, Resource Group, Region, Resource Type) SubsX tab, which will then automatically fill in the remaining 4 columns (Resource Group Migration Supported? Subscription Migration Supported? Resource Group Migration Limitations, Subscription Migration Limitations) that provide information about the ability to migrate the resources along with the migration limitations of each resource.

image

This spreadsheet provides a great overview of the resources in the subscription and identify what can be migrated and what cannot. It is important to note that the spreadsheet will place a Yes for classic resources even though it is not supported in a CSP migration so don't make the mistake of only displaying the items with No for the Subscription Migration Supported? column and assume the rest can be migrated:

Not supported in CSP - Must migrate to ARM model first.
Target subscription cannot contain any other classic resources.
All classic resources must be moved in one go.

image

Organization Subscriptions into Groups

The next step is to use the spreadsheet to separate subscriptions into different groups. The following are the 3 groups I use but it may not work for everyone so create as many or as little:

1. All resources can be migrated
2. All resources can be migrated but there are classic resources
3. Not all resources can be migrated

Group #1 - All resources can be migrated

Resources that can be migrated means that they can be moved without any service disruption to the resource during the move. However, subscriptions where all of the resources can be migrated does not mean you can navigate into portal.azure.com, click on All resources, select all the resources and move them. Even if a resource can be migrated cross subscriptions, it could have dependencies, which would require it to be moved with other resources or be placed in the resource group that it was originally created. It is also likely that some of the dependencies would mean downtime will be required. The following are the steps I perform for this group:

1. Organize all of the resource types in a spreadsheet and sort them by resource type

image

2. Log into portal.azure.com, launch Resource Explorer, navigate to Subscriptions > "Subscription Name" > Providers:

image

3. Have the Resource Explorer and spreadsheet side by side, analyze each type of resources to determine what the dependencies are. The reason why I like to use the Resource Explorer is because you can easily skip through the resource types that have no dependencies and click through each resource that do and use the Open blade button to navigate directly to the properties of the resource for further investigation

image

4. Create an action and notes column to the resources and fill in what actions need to be taken before moving them.

Migration Restrictions

The following are some of the restrictions that are likely to be encountered even if all the resources can be moved:

1. You can only move multiple resources if they are stored in the same resource group. It is not possible to select resources from multiple resource groups and move them.

2. When you move a virtual machine, dependent components such as the VNet the NIC is attached to also needs to be moved, which means if the VNet has a peering then it needs to be removed thus potentially causing downtime.

3. You cannot move a virtual machine and the associated VNet without moving any other virtual machines that are attached to the same VNet.

4. Azure App Services must be placed to original Resource Groups where they were created. If you are unsure where the App Service was originally created, you can use the following cmdlet to retrieve the serverFarmID that contains path of the original Resource Group:

$checkapp = Get-AzResource -ResourceGroupName <current_apps_RG_name> -Name <app_name> -ResourceType "Microsoft.Web/sites"

$checkapp.Properties.serverFarmId

5. Any IaaS VMs with backups configured need to have them disabled and with restore collection points deleted. Note that the deleting the restore collection points does not mean the data will be deleted. Use the PowerShell cmdlet in the following document to bulk delete the restore points: https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/move-limitations/virtual-machines-move-limitations#powershell

6. You cannot move a virtual machine without the public IP address that is attached to it. Note that it is possible to move Basic SKU public IP addresses but not Standard SKU public IP addresses.

7. You cannot move a VNet that is configured with an App Service with VNet integration.

8. When you move an App Service, you need to move its App Service Plan, which means you need to move all Apps in the same Apps Service Plan.

9. The destination subscription's resource group for an App Service to be moved cannot contain existing App Services.

10. When moving App Services or Functions, you cannot move them without moving other App Services or Functions in the same Resource Group.

11. You cannot move an App Service that has an uploaded but unbinded SSL certificate to another subscription. In order to move the App Service, we will need to move the certificate as well but the certificate object is not displayed in the resource group because it is a hidden resource so use the enable the Show hidden types option to select the certificate.

12. The increased quotas limits on the source subscriptions will need to be requested in the target

There are too many dependencies for me to list but careful analyzation of the resources should reveal all of them.

Group #2 - All resources can be moved but there are classic resources

The second group would contain resources that can be moved but with classic resources that need to be upgraded to ARM before they can be moved. I won't go into the details of upgrading classic resources to ARM as there are plenty of documentation on how this can be done but one of the scenarios I've come across is where a client had Cloud Apps in their subscription but it hosted an application that had been upgraded for other divisions. Rather than attempting to upgrade the Cloud Apps to ARM, we simply planned for deploying the newer version of the application in parallel then migrated over.

Group #3 - Not all resources can be moved

The last group, which likely would apply to most production environments, would contain resources that cannot be moved. The components I've typically come across are ones with the following resources:

1. Load Balancers with a Standard SKU

2. Public IP address with a Standard SKU

3. Azure Front Door

4. Private Endpoints

5. Azure Key Vaults with CMK and ADE

6. Azure SQL Managed Instances

7. ExpressRoute

8. Azure Monitor and Alerts

9. Subscriptions with different tenants

10. MarketPlace items with a plan (e.g. F5, CheckPoint, Fortinet, Citrix)

Every non-migratable components will need to be planned around and the following are a few options to provide ideas:

Load Balancers - Basic SKU Load Balancers can be migrated but no production environment would use basic SKU LBs. What I've typically done is deploy these resources in parallel, migrate one of the two, three or more backend servers to the destination subscription, add it to the new LB backend pool, make the new LB live and then migrate the remaining servers over. This can significantly reduce downtime.

Public IP Address - IPs with Standard SKUs cannot be migrated can have a new Public IP address configured in the destination subscription that is ready to be applied to migrated resources. Other alternatives could be to create a basic SKU IP address, assign it to the resource and move them together if the resource can be moved. More options are available for clusters so be creative.

Azure Front Door - These can be deployed in parallel and in the new subscription, and point them to, say, the App Services in the old subscriptions via public URL and migrate App Service over live.

Private Endpoints - These require more labour as you'll need to change all of them to use Service EndPoints, migrate the resources and then recreate them.

Azure Key Vaults - These can be a pain to migrate if the environment uses CMK and ADE as you'll need to decrypt the resources prior to moving them.

Azure SQL Managed Instances - These managed instances can be a headache as they're expensive but building new instances in parallel is likely the way to go for most cases.

ExpressRoute - These require a lot of planning as it is best to deploy in parallel but aside from the cost, these links can serve many services' ingress and egress traffic to on-premises datacenters or offices. Network address planning will also be required.

Azure Monitor and Alerts - Some of these cannot be migrated and therefore will need to be recreated.

Subscriptions with different tenants - the source and destination subscriptions need to be associated to the same tenant so if the destination a transfer may be required if they are not the same. Note that when you transfer a subscription to a different Azure AD directory, some resources are not transferred to the target directory. For example, all role assignments and custom roles in Azure role-based access control (Azure RBAC) are permanently deleted from the source directory and are not be transferred to the target directory.

Marketplace Items - These items can be laborious as you'll need to identify them in the current subscription and ensure they are also offered in the CSP subscription. The native Azure tools I've had luck with migrating these items are:

  • Disk snapshots: create snapshots of VMs, migrate snapshots to the new subscription, then a new VM with the same Marketplace image, and finally swap the disks with the migrated disks.
  • Azure Site Recovery: I generally prefer this method if possible as it requires much less manual labour than snapshots.
  • Note that there are many instances where a migrated Marketplace resources such as firewalls will require the license to be reapplied. Some WAFs such as Citrix NetScalers have their licenses bounded to the NIC's MAC so if that changes then a new license will need to get reallocated.

How well you plan around all the resources whether they can be migrated or not will dictate how successful the migration can be.

Clean up and delete unused resources

I’ve found that most of the environments I’ve worked in almost always have resources that are either orphaned or no longer needed and therefore can be deleted. Cleaning up resources and RGs will save a lot of time for the next step so delete any resources that are identified to not be used. Azure Resource Graph Explorer can help with identifying resources such as unattached disks and the following is a query that can be used across all subscriptions:

resources
| where type =~ 'Microsoft.Compute/disks'
| where properties.diskState =~ 'Unattached'
| project name, resourceGroup, subscriptionId, location, tenantId

Grouping resources into resource groups

Once all resources have been reviewed, the next step is to start organizing them into resource groups that will be migrated. Resources that need to be migrated together should be grouped into the same RG so all of them can be migrated in one move. Also don't forget that there are limitations on what the destination RG can have depending on what the source resources are being moved into it (e.g. you can't move App Service resources into a RG that already have App Services).

Test Migration of Resources

At the time of this writing (June 19, 2021), the migration feature within the Azure Portal randomly presents two methods where one will instantly move the resources selected if validation succeeds, while the other will halt and wait for the administrator to interactively click on the button to proceed. This will likely change in the future but if it hasn't, use the following two resources I mentioned earlier to test the migration:

Morten Pedholt provides his experience, migration planning tips and how to call the validation API to determine whether resources can be migrated from one subscription to another:

https://pedholtlab.com/migrate-between-azure-subscriptions-like-a-pro/

Pantelis Apostolidis provides a way to use Postman to run the resource migration validation:

https://www.cloudcorner.gr/microsoft/azure/validate-azure-resource-move-with-postman/

It is best to also create a test environment with two subscriptions so you can test the migration of resources in the environment. I’ve performed some tests for moving resources in the following two blog posts:

Moving an Azure virtual machine and the Recovery Services Vault with its backups from one subscription to another
http://terenceluk.blogspot.com/2021/04/moving-azure-virtual-machine-and.html

Moving Azure App Service resources from one subscription to another subscription
http://terenceluk.blogspot.com/2021/04/moving-app-service-resources-from-one.html

Identify any dependencies on the resource ID

One of the common items that can get missed with subscription migrations are any applications that integrate with Azure and reference resource IDs or subscription IDs. Given how the source and destination subscriptions are different, the resource ID of all resources will change.

Another common consideration I come across are any integration with the EA portal (ea.azure.com) for billing information as going to CSP means the EA portal will no longer provide subscription information.

Using Azure Resource Graph Explorer to help with migration planning

I've found that one of the best tools available for gathering information is Azure Resource Graph Explorer as you can query and filter results as you please.

List subscriptions with Classic Resources

You can quickly export a list of Classic Resources in one or all subscriptions that need to be upgraded/migrated to ARM with the following query:

Resources
| where type contains 'Microsoft.Classic'
| order by type asc

image

Look for Marketplace solutions
 
You can use the following query to query for marketplace solutions with plans:
 
resources
| where isnotnull(plan)
| sort by (type) asc

image

List non-Microsoft native resources

The following query can be used to look up non-Microsoft Azure native resources such as SendGrid, which would need to be recreated in the target subscription:
 
resources
| where type notcontains ('microsoft.')
| sort by (type) asc

image

List Load Balancers across subscriptions

The following query will assist with obtaining a list of load balancers across subscriptions:
 
resources
| where type contains 'Microsoft.Network/Loadbalancers'
| order by type asc

image

Retrieve list of VM disks configured with SSE with CMK

Obtaining a list of virtual machine disks configured with SSE with CMK Encryption:

resources
| where type =~ 'Microsoft.Compute/disks'
| where properties.encryption.type =~ 'EncryptionAtRestWithCustomerKey'

Retrieve list of VMs with disks configured with ADE

Obtain a list of virtual machines disks with Azure Disk Encryption (ADE) enabled, which would need to be disabled before migration:

resources
| where type =~ 'Microsoft.Compute/disks'
| where properties.encryptionSettingsCollection.enabled =~ 'true'

I've been very excited ever since Azure Resource Graph Explorer became available as it made viewing, filtering, searching for resources so convenient and it has proven to be a very useful tool for analyzing subscriptions. Refer to the following documentation for more information:
 
Starter Resource Graph query samples
https://docs.microsoft.com/en-us/azure/governance/resource-graph/samples/starter?tabs=azure-cli

Post Migration Tasks 

The post migration tasks to consider are as follows:

1. Allow customer to view charges as pay-as-you-go pricing (configured in the portal.azure.com portal under the Policies settings)

2. Allow customer to request Reserved Instances (configured in the Partner Central portal)

3. Grant the Foreign Principal representing the CSP provider (IBM, TechData, etc) to the migrated subscriptions so tickets can be opened by the CSP (this is specific to subscriptions that were migrated with the MSP Expert tool)

Granting a CSP Foreign Principal the Reader or Owner role onto an Azure Subscription with PowerShell
http://terenceluk.blogspot.com/2021/09/granting-csp-foreign-principal-reader.html

----------------------------------------------------------------------------------------------------------------

I'm sure there are more considerations that I may not have covered but I hope I can contribute to the limited information around planning available for this type of project.

Attempting to upgrade ESXi on a Nutanix node manually fails with: "OSError: [Errno 39 Directory not empty: ‘/vmfs/volumes/e2be1277-f8844313-564e-dbe0fc3821c4/Nutanix’"

Problem

You’re attempting to manually upgrade a Nutanix node from VMware ESXi, 6.5.0, 17167537 to VMware ESXi, 6.7.0, 15160138 with the ISO VMware-VMvisor-Installer-201912001-15160138.x86_64.iso then patch it to VMware ESXi, 6.7.0, 17700523 with ESXi670-202103001.zip:

image

Upgrade ESXi, preserve VMFS datastore

image

image

… but the upgrade from 6.5 to 6.7 would fail with the following error after you select the upgrade option:

------ An unexpected error occurred ------

See logs for details

OSError: [Errno 39 Directory not empty: ‘/vmfs/volumes/e2be1277-f8844313-564e-dbe0fc3821c4/Nutanix’

image

The upgrade does not proceed further until you restart the host, which will boot into ESXi 6.5 normally.

Subsequent attempts to re-run the upgrade will now present the option of installing a fresh ESXi installation and not upgrade.

Install ESXi, preserve VMFS datastore

image

Solution

I could not find any Nutanix KB but I did find quite a few forum and blog posts indicating that the files in /bootbank need to be copied to /altbootbank (copy not move) so I performed the following with WinSCP:

  1. Backed up single file in /altbootbank named boot.cfg.backup and then deleted it
  2. Backed up the Nutanix folder in bootbank folder and then deleted it
  3. Copied the files in /bootbank to /altbootbank

Note that removing the Nutanix folder is important as failure to do so will not allow you to upgrade.

bootbank directory:

image

image

altbookbank directory:

image

I then rebooted the ESXi host with the 6.7 ISO mounted and was able to proceed with the upgrade and proceed to patch 6.7.

image

To be safe, I performed the following after the upgrade:

  1. Copy the file named boot.cfg.backup back into in /altbootbank
  2. Copy the the Nutanix folder back into the bootbank folder

Proceeded to upgrade

esxcli software vib install -d /vmfs/volumes/ntnx-lun03/ISO/ESXi670-202103001.zip

image

Hope this helps anyone who may encounter this issue.

Saturday, June 12, 2021

How to configure Citrix ADC / NetScaler to forward client Source IP to Exchange Server 2019 / 2016 or any IIS application

Those who have worked with load balancers for applications will know that it can be a pain to troubleshoot issue where the source IP address is required because from the application’s perspective, all incoming connections have the originating IP address as the load balancer. With Citrix ADC / NetScalers, there are several methods in achieving this such as using the X-Forwarded-For header to include the source client IP address (this only works with HTTP and SSL services) or configuring direct server return (DSR) mode to allow the server to respond to clients directly by using a return path that does not flow through the Citrix ADC appliance. There are advantages and disadvantages for each method but for the purpose of this post, I will demonstrate how to configure Exchange Server 2019 (or any IIS application) to receive the source client IP with the X-Forwarded-For header.

The Scenario

Let’s assume that you have a user who is continuously locked out of their account and you have identified the event to take place on an on-premise Exchange server as you can see event ID 4625 Audit Failure events in the Security log as shown in the screenshot below:

image

The Exchange server is placed behind Citrix ADC / NetScalers and therefore have the IP address 172.16.5.90 of the load balancer for the Source Network Address field in the event.

Proceeding to navigate into the IIS logs on the Exchange server in the W3SVC1 folder located in the C:\inetpub\logs\LogFiles\ directory:

image

… and opening the logs show only the source IP of the Citrix ADC / NetScaler:

image

image

Configuration IIS on Exchange Server to log the X-Forwarded-For request header

The first step to log the source IP address is to configure IIS on the Exchange server to log the X-Forwarded-For request header that is passed from the Citrix ADC / NetScaler load balancer. The following TechNet Blog does a fantastic job of demonstrating the process:

How to use X-Forwarded-For header to log actual client IP address?
https://techcommunity.microsoft.com/t5/iis-support-blog/how-to-use-x-forwarded-for-header-to-log-actual-client-ip/ba-p/873115

Below is a demonstration with an Exchange 2019 server on Windows Server 2019 and IIS version 10.0.17763.1:

image

Begin by launching Internet Information (IIS) Manager, navigate to either the Server node or one of the websites and then open on Logging:

image

image

We will add the X-Forwarded-For field by clicking on Select Fields beside the W3C Format dropdown menu:

image

Proceed to click on Add Field and add the X-Forwarded-For text as the Field Name and Source, with the Source Type as Request Header:

image

Note how the X-Forwarded-For is added as a Custom Field:

image

Apply the changes:

image

**Note that configuring the above on one site automatically applies it to the other sites.

Now navigate back to the IIS log files and open the latest log file and confirm that the X-Forwarded-For field is added as a header:

image

The following is a side by side comparison where the log on the top has the X-Forwarded-For custom field added and the bottom does not:

image

Configure the Citrix ADC / NetScaler to forward client source IP as X-Forwarded-For

With the IIS server configured to receive the custom X-Forwarded-For field, proceed to log into the Citrix ADC / NetScaler, navigate to Traffic Management > Load Balancing > Service Groups or Services:

image

For the purpose of this example, we will be configuring all of the Exchange service groups to forward the client source IP address as X-Forwarded-For (owa, activesync, rpc, ews, Autodiscover, oab, mapi, ecp).

Open the properties of the load balancing service group or service, navigate to the Settings area and click on the edit icon:

image

Enable the Insert Client IP Header and type in the X-Forwarded-For string for the Header text box:

image

Click OK to save the settings and proceed to save the settings by clicking Done.

Repeat for the rest of the Load Balancing Service Group by using the GUI or the CLI command:

set service <name> -CIP <Value> <cipHeader>

Here are the commands for each Exchange service:

set serviceGroup SVG_EX2019_owa -cip enabled X-Forwarded-For
set serviceGroup SVG_EX2019_activesync -cip enabled X-Forwarded-For
set serviceGroup SVG_EX2019_rpc -cip enabled X-Forwarded-For
set serviceGroup SVG_EX2019_ews -cip enabled X-Forwarded-For
set serviceGroup SVG_EX2019_autodiscover -cip enabled X-Forwarded-For
set serviceGroup SVG_EX2019_oab -cip enabled X-Forwarded-For
set serviceGroup SVG_EX2019_mapi -cip enabled X-Forwarded-For
set serviceGroup SVG_EX2019_ecp -cip enabled X-Forwarded-For

image

Testing the configuration by verifying source IP address in IIS Logs

Switching back to the Exchange Server and navigating to the IIS logs should now have the latest log reveal a value for the X-Forwarded-For field. Below is a screenshot of the log before the configuration of the Citrix ADC / NetScaler:

image

Below is a screenshot after the change with an IP address added to the end of each connection with the source IP:

image

Hope this helps anyone looking for a way to log the originating source IP address of client requests on IIS that is load balanced by a Citrix ADC / NetScaler.

Tuesday, June 8, 2021

Using AirWatch to remotely run batch files for uninstalling applications

I don’t get to work with MDM or UEM applications much anymore due to my focus on Azure so I got pretty excited when an ex-colleague asked me if there was a way to use VMware AirWatch (also known as Workspace One) to remotely execute an uninstall command for an application because I had gone through the process before. After digging up some old notes to guide him through the setup, I thought I’d write this blog post in case anyone else happens to ask me in the future.

The official documentation, although a bit outdated, for this feature can be found here:

Using Product Provisioning to Deliver Files to Windows 10: Workspace ONE Operational Tutorial
https://techzone.vmware.com/using-product-provisioning-deliver-files-windows-10-workspace-one-operational-tutorial#_991596

For the purpose of this example, I will be using a batch file that manually (and forcefully) removes Cylance Protect from devices. Not long ago I ran into an issue where attempting to uninstall Cylance Protect from devices would display the following error:

Cylance PROTECT
You are attempting to run the 32-bit installer on a 64-bit version of Windows. Please run the 64-bit installer.

image

I couldn’t determine how to get around this as none of the methods such as editing registry keys and executing the following msiexec.exe would work:

msiexec /x {2E64FC5C-9286-4A31-916B-0D8AE4B22954} /quiet

Reaching out to Blackberry support had an support engineer point me to:

Fix problems that block programs from being installed or removed

https://support.microsoft.com/en-us/mats/program_install_and_uninstall

The tool worked but it had to be ran interactively and did not allow me to use it at scale.

A bit more research led me to the following script written by James Gallagher, which worked but please note that as this was originally provided by Cylance but later modified by Cyberforce, use this at your own risk. It worked for me but may not for others.

Manual Removal Of CylancePROTECT
https://cyberforcesecurityhelp.freshdesk.com/support/solutions/articles/44002036687-manual-removal-of-cylanceprotect

In case the post ever gets deleted, I will paste the contents for the customized-CylanceCleanupTool.bat at the end of this post.

With the above scenario described, let’s begin creating the configuration in AirWatch Version: 20.11.0.5 (2011):

image

Create the Files/Actions

The first step is to create a Files/Actions that will allow you to upload the batch file, define where to store it on the device, and how to execute the batch file.

Begin by navigating to Devices > Provisioning > Components > Files/Actions:

image

Click on the ADD FILES/ACTIONS:

image

Select Windows under Add Files/Actions:

image

Select Windows Desktop under Select Device Type:

image

Type in a name for the action and select the appropriate organization for Managed By:

image

Navigate to the Files tab, click on the ADD FILES tab, then Choose Files to select the batch file that will be uploaded and pushed to the clients:

image

image

Specify a download path where the batch file will be downloaded to on the client:

C:\Temp\AirWatch\

image

Save the configuration and the following line will be displayed in the Files tab:

image

Navigate to the Manifest tab and click on Install Manifest:

image

Select Run for Action(s) To Perform:

image

Select System for the Execution Context so the batch file is running with elevated permissions and specify the path and batch file (the location specified earlier for downloading the batch file and the batch file name that was just uploaded:

C:\Temp\AirWatch\customized-CylanceCleanupTool.bat

image

The following configuration will be displayed under Install Manifest. We can specify a command to uninstall but there will not be one configured for this example:

image

Proceed to save the new Files/Actions:

image

Create the Product to assign to devices

With the creation of the Files/Actions completed, the next step is to assign it to devices.

Navigate to Devices > Provisioning > Product List View and click on ADD PRODUCT:

image

Select Windows under Add Product:

image

Select Windows Desktop under Select Device Type:

image

Provide a name and description for the product, select the appropriate organization, and select the Smart Group this product should be applied to. For the purpose of this example, we will be assigning it to all devices.

image

Click on the Manifest tab and then the ADD button:

image

Select File/Action - Install for Action(s) To Perform and the previously created Files/Actions for the Files/Actions field:

image

The saved Manifest will be displayed as such:

image

You can further specify Conditions, Deployment and Dependencies options:

image

image

image

With the configuration completed, decide to click Save button to simply save the Manifest or Activate to save and immediately activate the configuration:

image

For the purpose of this example, I will click on Activate, which will display the list of devices it will be applied to:

image

The new product should now be displayed:

image

Waiting a few seconds and refreshing the update the In Progress, Compliant and Failed values:

image

Hope this helps anyone who might be looking for instructions on how to remotely run batch files with AirWatch.

customized-CylanceCleanupTool.bat

@ECHO OFF

title UNIFIED DRIVER CYLANCE CLEANUP TOOL

Echo UNIFIED DRIVER CYLANCE CLEANUP TOOL

:SwitchToWorkingDirectory

cd /d "%~dp0" 1>nul 2>&1

:AdminCheck

openfiles>nul 2>&1

IF %ERRORLEVEL% == 0 (

:AdminCheckPass

GOTO ManualUninstall

) ELSE (

:AdminCheckFail

Echo * Please re-run the Unified Driver Cylance Cleanup Tool as Administrator.

Echo * Exiting...

GOTO CyExit

)

:ManualUninstall

reg delete HKEY_LOCAL_MACHINE\SOFTWARE\Cylance /f

reg delete HKEY_CLASSES_ROOT\Installer\Products\C5CF46E2682913A419B6D0A84E2B9245 /f

reg delete HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\CylanceSvc /f

taskkill /im CylanceUI.exe

takeown /f "C:\Program Files\Cylance" /r /d y

icacls "C:\Program Files\Cylance" /reset /T

rd /s /q "C:\Program Files\Cylance"

takeown /f "C:\programdata\Cylance" /r /d y

rd /s /q C:\programdata\Cylance

:InstallCleanup

Echo * Installing the Unified Driver Cylance Cleanup Tool service...

CyCleanupSvc.exe "-install"

IF %ERRORLEVEL% == 0 (

GOTO WaitCleanup

) ELSE (

Echo * Failed to install the Unified Driver Cylance Cleanup Tool service.

Echo * Please check the Logs directory.

Echo * Exiting...

GOTO CyExit

)

:WaitCleanup

Echo * Waiting for the Unified Driver Cylance Cleanup Tool service to cleanup...

ping -n 30 127.0.0.1 1>nul 2>&1

Echo * Unified Driver Cylance Cleanup Tool is finished.

Echo * Removing the Unified Driver Cylance Cleanup Tool service...

CyCleanupSvc.exe "-uninstall"

IF %ERRORLEVEL% == 0 (

GOTO FinishCleanup

) ELSE (

Echo * Failed to remove the Unified Driver Cylance Cleanup Tool service.

Echo * Please check the Logs directory.

Echo * Exiting...

GOTO CyExit

)

:FinishCleanup

Echo * Unified Driver Cylance Cleanup Tool service has been removed.

Echo * Exiting...

:CyExit

exit