Repository: Azure/ARO-RP
Pull request #3710 (2024-07-20 00:17:05)

Always do group expansion for CheckAccess subjects
### Which issue this PR addresses:

Fixes: ARO-9261

### What this PR does / why we need it:
Refer to the IcM in JIRA, CheckAccess team informed that we need to send an additional field to give hints to CheckAccess that "group expansion" is needed.

Without group expansion, CheckAccess will not take into account what groups the principal belongs to.  That is, if a service principal A is a member of group B, and group B allows writing to a VNet, calling CheckAccess without the group expansion field will return a response that A is not allowed to write to a VNet.  With the group expansion hint, CheckAccess will return an allowed response, which is the behavior we want.

### Test plan for issue:
I reproduced this by creating a Deny Assignment on a VNet resource from Azure Blueprint and made a group excluded from it.  The testing app principal is then added to the group.  After that I verified the following 2 scenarios:
1. Without group expansion, CheckAccess will return some actions as denied.  Here are the request, the response and the formatted response:
**Request** (notice the lack of the new __Claim_Name_ field in Subject)
```json
{
    "Subject": {
        "Attributes": {
            "ObjectId": "69f8005a-36fe-4aa7-b161-c435d34133ad"
        }
    },
    "Actions": [
        {
            "Id": "Microsoft.ClassicNetwork/networkSecurityGroups/securityRules/write",
            "Attributes": null
        },
        {
            "Id": "Microsoft.Network/virtualNetworks/join/action",
            "Attributes": {}
        },
        {
            "Id": "Microsoft.Network/virtualNetworks/read",
            "Attributes": {}
        },
        {
            "Id": "Microsoft.Network/virtualNetworks/write",
            "Attributes": {}
        },
        {
            "Id": "Microsoft.Network/virtualNetworks/subnets/join/action",
            "Attributes": {}
        },
        {
            "Id": "Microsoft.Network/virtualNetworks/subnets/read",
            "Attributes": {}
        },
        {
            "Id": "Microsoft.Network/virtualNetworks/subnets/write",
            "Attributes": {}
        }
    ],
    "Resource": {
        "Id": "/subscriptions/60bf318d-6914-4105-a3b5-d0d2c10388c8/resourceGroups/nont-test-assignment/providers/Microsoft.Network/virtualNetworks/net-wgssmdvlnix4m",
        "Attributes": {}
    },
    "Environment": {
        "Attributes": null
    }
}
```
**Response** (Notice that some actions were reported as denied)
```json
{
    "value": [
        {
            "accessDecision": "Denied",
            "actionId": "Microsoft.ClassicNetwork/networkSecurityGroups/securityRules/write",
            "isDataAction": false,
            "roleAssignment": null,
            "denyAssignment": {
                "isSystemProtected": true,
                "id": "d778a6b1157f2abbbe280825f36196f1",
                "name": "",
                "description": "",
                "permissions": [
                    {
                        "actions": [
                            "*"
                        ],
                        "notActions": [
                            "Microsoft.Authorization/locks/delete",
                            "*/read",
                            "Microsoft.Network/virtualNetworks/subnets/join/action"
                        ],
                        "dataActions": [],
                        "notDataActions": [],
                        "condition": "",
                        "conditionVersion": ""
                    }
                ],
                "scope": "/subscriptions/60bf318d-6914-4105-a3b5-d0d2c10388c8/resourceGroups/nont-test-assignment/providers/Microsoft.Network/virtualNetworks/net-wgssmdvlnix4m",
                "doNotApplyToChildScopes": false,
                "principals": [
                    {
                        "id": "00000000000000000000000000000000",
                        "type": "SystemDefined"
                    }
                ],
                "excludePrincipals": [
                    {
                        "id": "864512b599134a29b4c9ea8d1117f181",
                        "type": "ServicePrincipal"
                    },
                    {
                        "id": "1cae329ebf0b4456b8d4d74b1c5e7077",
                        "type": "Group"
                    }
                ],
                "condition": "",
                "conditionVersion": "",
                "effect": null
            },
            "timeToLiveInMs": 60000
        },
        {
            "accessDecision": "Denied",
            "actionId": "Microsoft.Network/virtualNetworks/join/action",
            "isDataAction": false,
            "roleAssignment": null,
            "denyAssignment": {
                "isSystemProtected": true,
                "id": "d778a6b1157f2abbbe280825f36196f1",
                "name": "",
                "description": "",
                "permissions": [
                    {
                        "actions": [
                            "*"
                        ],
                        "notActions": [
                            "Microsoft.Authorization/locks/delete",
                            "*/read",
                            "Microsoft.Network/virtualNetworks/subnets/join/action"
                        ],
                        "dataActions": [],
                        "notDataActions": [],
                        "condition": "",
                        "conditionVersion": ""
                    }
                ],
                "scope": "/subscriptions/60bf318d-6914-4105-a3b5-d0d2c10388c8/resourceGroups/nont-test-assignment/providers/Microsoft.Network/virtualNetworks/net-wgssmdvlnix4m",
                "doNotApplyToChildScopes": false,
                "principals": [
                    {
                        "id": "00000000000000000000000000000000",
                        "type": "SystemDefined"
                    }
                ],
                "excludePrincipals": [
                    {
                        "id": "864512b599134a29b4c9ea8d1117f181",
                        "type": "ServicePrincipal"
                    },
                    {
                        "id": "1cae329ebf0b4456b8d4d74b1c5e7077",
                        "type": "Group"
                    }
                ],
                "condition": "",
                "conditionVersion": "",
                "effect": null
            },
            "timeToLiveInMs": 60000
        },
        {
            "accessDecision": "Allowed",
            "actionId": "Microsoft.Network/virtualNetworks/read",
            "isDataAction": false,
            "roleAssignment": {
                "delegatedManagedIdentityResourceId": "",
                "id": "f7268f5c440747728341363f5446d35f",
                "roleDefinitionId": "b24988ac618042a0ab8820f7382dd24c",
                "principalId": "69f8005a36fe4aa7b161c435d34133ad",
                "principalType": "ServicePrincipal",
                "scope": "/subscriptions/60bf318d-6914-4105-a3b5-d0d2c10388c8",
                "condition": "",
                "conditionVersion": "",
                "canDelegate": false,
                "description": ""
            },
            "denyAssignment": null,
            "timeToLiveInMs": 60000
        },
        {
            "accessDecision": "Denied",
            "actionId": "Microsoft.Network/virtualNetworks/write",
            "isDataAction": false,
            "roleAssignment": null,
            "denyAssignment": {
                "isSystemProtected": true,
                "id": "d778a6b1157f2abbbe280825f36196f1",
                "name": "",
                "description": "",
                "permissions": [
                    {
                        "actions": [
                            "*"
                        ],
                        "notActions": [
                            "Microsoft.Authorization/locks/delete",
                            "*/read",
                            "Microsoft.Network/virtualNetworks/subnets/join/action"
                        ],
                        "dataActions": [],
                        "notDataActions": [],
                        "condition": "",
                        "conditionVersion": ""
                    }
                ],
                "scope": "/subscriptions/60bf318d-6914-4105-a3b5-d0d2c10388c8/resourceGroups/nont-test-assignment/providers/Microsoft.Network/virtualNetworks/net-wgssmdvlnix4m",
                "doNotApplyToChildScopes": false,
                "principals": [
                    {
                        "id": "00000000000000000000000000000000",
                        "type": "SystemDefined"
                    }
                ],
                "excludePrincipals": [
                    {
                        "id": "864512b599134a29b4c9ea8d1117f181",
                        "type": "ServicePrincipal"
                    },
                    {
                        "id": "1cae329ebf0b4456b8d4d74b1c5e7077",
                        "type": "Group"
                    }
                ],
                "condition": "",
                "conditionVersion": "",
                "effect": null
            },
            "timeToLiveInMs": 60000
        },
        {
            "accessDecision": "Allowed",
            "actionId": "Microsoft.Network/virtualNetworks/subnets/join/action",
            "isDataAction": false,
            "roleAssignment": {
                "delegatedManagedIdentityResourceId": "",
                "id": "f7268f5c440747728341363f5446d35f",
                "roleDefinitionId": "b24988ac618042a0ab8820f7382dd24c",
                "principalId": "69f8005a36fe4aa7b161c435d34133ad",
                "principalType": "ServicePrincipal",
                "scope": "/subscriptions/60bf318d-6914-4105-a3b5-d0d2c10388c8",
                "condition": "",
                "conditionVersion": "",
                "canDelegate": false,
                "description": ""
            },
            "denyAssignment": null,
            "timeToLiveInMs": 60000
        },
        {
            "accessDecision": "Allowed",
            "actionId": "Microsoft.Network/virtualNetworks/subnets/read",
            "isDataAction": false,
            "roleAssignment": {
                "delegatedManagedIdentityResourceId": "",
                "id": "f7268f5c440747728341363f5446d35f",
                "roleDefinitionId": "b24988ac618042a0ab8820f7382dd24c",
                "principalId": "69f8005a36fe4aa7b161c435d34133ad",
                "principalType": "ServicePrincipal",
                "scope": "/subscriptions/60bf318d-6914-4105-a3b5-d0d2c10388c8",
                "condition": "",
                "conditionVersion": "",
                "canDelegate": false,
                "description": ""
            },
            "denyAssignment": null,
            "timeToLiveInMs": 60000
        },
        {
            "accessDecision": "Denied",
            "actionId": "Microsoft.Network/virtualNetworks/subnets/write",
            "isDataAction": false,
            "roleAssignment": null,
            "denyAssignment": {
                "isSystemProtected": true,
                "id": "d778a6b1157f2abbbe280825f36196f1",
                "name": "",
                "description": "",
                "permissions": [
                    {
                        "actions": [
                            "*"
                        ],
                        "notActions": [
                            "Microsoft.Authorization/locks/delete",
                            "*/read",
                            "Microsoft.Network/virtualNetworks/subnets/join/action"
                        ],
                        "dataActions": [],
                        "notDataActions": [],
                        "condition": "",
                        "conditionVersion": ""
                    }
                ],
                "scope": "/subscriptions/60bf318d-6914-4105-a3b5-d0d2c10388c8/resourceGroups/nont-test-assignment/providers/Microsoft.Network/virtualNetworks/net-wgssmdvlnix4m",
                "doNotApplyToChildScopes": false,
                "principals": [
                    {
                        "id": "00000000000000000000000000000000",
                        "type": "SystemDefined"
                    }
                ],
                "excludePrincipals": [
                    {
                        "id": "864512b599134a29b4c9ea8d1117f181",
                        "type": "ServicePrincipal"
                    },
                    {
                        "id": "1cae329ebf0b4456b8d4d74b1c5e7077",
                        "type": "Group"
                    }
                ],
                "condition": "",
                "conditionVersion": "",
                "effect": null
            },
            "timeToLiveInMs": 60000
        }
    ],
    "nextLink": null
}
```
2. With group expansion hint
**Request** (Notice the additional field in Subject)
```json
{
    "Subject": {
        "Attributes": {
            "ObjectId": "69f8005a-36fe-4aa7-b161-c435d34133ad",
            "_claim_names": "{\"groups\":\"ENGORGIO~!\"}"
        }
    },
    "Actions": [
        {
            "Id": "Microsoft.ClassicNetwork/networkSecurityGroups/securityRules/write",
            "Attributes": null
        },
        {
            "Id": "Microsoft.Network/virtualNetworks/join/action",
            "Attributes": {}
        },
        {
            "Id": "Microsoft.Network/virtualNetworks/read",
            "Attributes": {}
        },
        {
            "Id": "Microsoft.Network/virtualNetworks/write",
            "Attributes": {}
        },
        {
            "Id": "Microsoft.Network/virtualNetworks/subnets/join/action",
            "Attributes": {}
        },
        {
            "Id": "Microsoft.Network/virtualNetworks/subnets/read",
            "Attributes": {}
        },
        {
            "Id": "Microsoft.Network/virtualNetworks/subnets/write",
            "Attributes": {}
        }
    ],
    "Resource": {
        "Id": "/subscriptions/60bf318d-6914-4105-a3b5-d0d2c10388c8/resourceGroups/nont-test-assignment/providers/Microsoft.Network/virtualNetworks/net-wgssmdvlnix4m",
        "Attributes": {}
    },
    "Environment": {
        "Attributes": null
    }
}
```

**Response** (all actions are allowed)
```json
{
    "value": [
        {
            "accessDecision": "Allowed",
            "actionId": "Microsoft.ClassicNetwork/networkSecurityGroups/securityRules/write",
            "isDataAction": false,
            "roleAssignment": {
                "delegatedManagedIdentityResourceId": "",
                "id": "f7268f5c440747728341363f5446d35f",
                "roleDefinitionId": "b24988ac618042a0ab8820f7382dd24c",
                "principalId": "69f8005a36fe4aa7b161c435d34133ad",
                "principalType": "ServicePrincipal",
                "scope": "/subscriptions/60bf318d-6914-4105-a3b5-d0d2c10388c8",
                "condition": "",
                "conditionVersion": "",
                "canDelegate": false,
                "description": ""
            },
            "denyAssignment": null,
            "timeToLiveInMs": 60000
        },
        {
            "accessDecision": "Allowed",
            "actionId": "Microsoft.Network/virtualNetworks/join/action",
            "isDataAction": false,
            "roleAssignment": {
                "delegatedManagedIdentityResourceId": "",
                "id": "f7268f5c440747728341363f5446d35f",
                "roleDefinitionId": "b24988ac618042a0ab8820f7382dd24c",
                "principalId": "69f8005a36fe4aa7b161c435d34133ad",
                "principalType": "ServicePrincipal",
                "scope": "/subscriptions/60bf318d-6914-4105-a3b5-d0d2c10388c8",
                "condition": "",
                "conditionVersion": "",
                "canDelegate": false,
                "description": ""
            },
            "denyAssignment": null,
            "timeToLiveInMs": 60000
        },
        {
            "accessDecision": "Allowed",
            "actionId": "Microsoft.Network/virtualNetworks/read",
            "isDataAction": false,
            "roleAssignment": {
                "delegatedManagedIdentityResourceId": "",
                "id": "f7268f5c440747728341363f5446d35f",
                "roleDefinitionId": "b24988ac618042a0ab8820f7382dd24c",
                "principalId": "69f8005a36fe4aa7b161c435d34133ad",
                "principalType": "ServicePrincipal",
                "scope": "/subscriptions/60bf318d-6914-4105-a3b5-d0d2c10388c8",
                "condition": "",
                "conditionVersion": "",
                "canDelegate": false,
                "description": ""
            },
            "denyAssignment": null,
            "timeToLiveInMs": 60000
        },
        {
            "accessDecision": "Allowed",
            "actionId": "Microsoft.Network/virtualNetworks/write",
            "isDataAction": false,
            "roleAssignment": {
                "delegatedManagedIdentityResourceId": "",
                "id": "f7268f5c440747728341363f5446d35f",
                "roleDefinitionId": "b24988ac618042a0ab8820f7382dd24c",
                "principalId": "69f8005a36fe4aa7b161c435d34133ad",
                "principalType": "ServicePrincipal",
                "scope": "/subscriptions/60bf318d-6914-4105-a3b5-d0d2c10388c8",
                "condition": "",
                "conditionVersion": "",
                "canDelegate": false,
                "description": ""
            },
            "denyAssignment": null,
            "timeToLiveInMs": 60000
        },
        {
            "accessDecision": "Allowed",
            "actionId": "Microsoft.Network/virtualNetworks/subnets/join/action",
            "isDataAction": false,
            "roleAssignment": {
                "delegatedManagedIdentityResourceId": "",
                "id": "f7268f5c440747728341363f5446d35f",
                "roleDefinitionId": "b24988ac618042a0ab8820f7382dd24c",
                "principalId": "69f8005a36fe4aa7b161c435d34133ad",
                "principalType": "ServicePrincipal",
                "scope": "/subscriptions/60bf318d-6914-4105-a3b5-d0d2c10388c8",
                "condition": "",
                "conditionVersion": "",
                "canDelegate": false,
                "description": ""
            },
            "denyAssignment": null,
            "timeToLiveInMs": 60000
        },
        {
            "accessDecision": "Allowed",
            "actionId": "Microsoft.Network/virtualNetworks/subnets/read",
            "isDataAction": false,
            "roleAssignment": {
                "delegatedManagedIdentityResourceId": "",
                "id": "f7268f5c440747728341363f5446d35f",
                "roleDefinitionId": "b24988ac618042a0ab8820f7382dd24c",
                "principalId": "69f8005a36fe4aa7b161c435d34133ad",
                "principalType": "ServicePrincipal",
                "scope": "/subscriptions/60bf318d-6914-4105-a3b5-d0d2c10388c8",
                "condition": "",
                "conditionVersion": "",
                "canDelegate": false,
                "description": ""
            },
            "denyAssignment": null,
            "timeToLiveInMs": 60000
        },
        {
            "accessDecision": "Allowed",
            "actionId": "Microsoft.Network/virtualNetworks/subnets/write",
            "isDataAction": false,
            "roleAssignment": {
                "delegatedManagedIdentityResourceId": "",
                "id": "f7268f5c440747728341363f5446d35f",
                "roleDefinitionId": "b24988ac618042a0ab8820f7382dd24c",
                "principalId": "69f8005a36fe4aa7b161c435d34133ad",
                "principalType": "ServicePrincipal",
                "scope": "/subscriptions/60bf318d-6914-4105-a3b5-d0d2c10388c8",
                "condition": "",
                "conditionVersion": "",
                "canDelegate": false,
                "description": ""
            },
            "denyAssignment": null,
            "timeToLiveInMs": 60000
        }
    ],
    "nextLink": null
}
```

### Is there any documentation that needs to be updated for this PR?
N/A - the code should be clear enough.

### How do you know this will function as expected in production? 
E2E tests should be enough.

nwnt: /azp run e2e,ci
azure-pipelines[bot]: <samp>
Azure Pipelines successfully started running 2 pipeline(s).<br>

</samp>
nwnt: /azp run ci
azure-pipelines[bot]: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>

Patch:
diff --git a/.gitignore b/.gitignore
index 0ff89866f..d51d176e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,3 +42,4 @@ megalinter-reports/
 /jq
 /portalauth
 .kiota.log
+/clusterapp.env
diff --git a/.pipelines/rp-full-dev-setup.yml b/.pipelines/rp-full-dev-setup.yml
deleted file mode 100644
index 6ee3a2b20..000000000
--- a/.pipelines/rp-full-dev-setup.yml
+++ /dev/null
@@ -1,94 +0,0 @@
-trigger: none
-pr: none
-
-# Parameters
-# ---------
-parameters:
-  - template: ./rp-dev/rp-dev-params.yml
-
-resources:
-  containers:
-    - container: container
-      image: registry.access.redhat.com/ubi8/toolbox:8.10
-      options: --user=0 --privileged -v /dev/shm:/dev/shm --device /dev/net/tun --name rp-dev-container # Root (and full sysetme) user privileges, shared memory between host and container, and access to the TUN device for network tunneling
-
-# Variables
-# ---------
-
-variables:
-  - template: ./rp-dev/rp-dev-vars.yml
-
-jobs:
-  - job: Full_RP_dev_setup_deployment
-    displayName: Deploy Full RP Dev
-    timeoutInMinutes: 180
-    pool:
-      name: 1es-aro-ci-pool
-
-    steps:
-# setup
-      # 1) Checkout repo
-      - template: ./templates/template-checkout.yml
-
-      # 2) Login to Azure with az CLI
-      - template: ./templates/template-az-cli-login.yml # does it have az CLI alraedy?
-        parameters:
-          azureDevOpsJSONSPN: ${{ parameters.azureJson }}
-
-      # 3) Generate RP Dev configuration file
-      - template: ./templates/rp-dev/template-generate-rp-dev-config.yml
-        parameters:
-          secretSA: ${{ parameters.secretSA }}
-          location: ${{ parameters.location }}
-          resourceGroup: ${{ parameters.resourceGroup }}
-          databaseAccountName: ${{ parameters.databaseAccountName }}
-          keyvaultPrefix: ${{ parameters.keyvaultPrefix }}
-          aroImage: ${{ parameters.aroImage }}
-
-      # 4) Semi deploy ...
-      - script: make deploy # use different target which will succeed and won't fail
-        name: semi-deploy-aro-rp-dev
-        displayName: 🚀 semi-deploy RP
-
-      # 5) Deploy VPN Gateway & AKS
-      - script: |
-          source ./hack/devtools/deploy-shared-env.sh
-          deploy_vpn_for_dedicated_rp
-          deploy_aks_dev
-        name: deploy-vpn-gateway
-        displayName: 🚀 Deploy VPN Gateway and AKS
-
-      # 6) Install Hive
-      - template: ./templates/rp-dev/template-install-hive.yml
-        parameters:
-          location: ${{ parameters.location }}
-
-      # 7) Mirror repos to ACR
-      - template: ./templates/rp-dev/template-mirror-images_to_acr.yml
-        parameters:
-          mirrorTag: ${{ parameters.ocpVersionTag }}
-          dst_acr_name: ${{ parameters.dst_acr_name }}
-
-      # 8) Final preperation before deploying RP
-      - template: ./templates/rp-dev/template-prepare-rp-deploy.yml
-        parameters:
-          rpResourceGroup: ${{ parameters.resourceGroup }}
-          rpVmssName: rp-vmss-
-          gwyResourceGroup: $(AZURE_UNIQUE_PREFIX)-gwy-${{ parameters.location }}
-          gwyVmssName: gateway-vmss-
-
-      # 9) Fully deploy RP
-      - script: make deploy
-        name: full-deploy-aro-rp-dev
-        displayName: 🚀 Fully deploy ARO RP Setup
-
-      # 10) Create SA and Role assignment
-      - script: |
-          source ./hack/devtools/deploy-shared-env.sh
-          deploy_oic_for_dedicated_rp
-        name: create-sa-role
-        displayName: ⚙️ Create storage account and role assignment
-## few more steps would be added (e.g., cluster creation)
-
-# cleanup
-      - template: ./templates/template-az-cli-logout.yml
diff --git a/.pipelines/templates/rp-dev/rp-dev-params.yml b/.pipelines/templates/rp-dev/rp-dev-params.yml
deleted file mode 100644
index 982a09401..000000000
--- a/.pipelines/templates/rp-dev/rp-dev-params.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-parameters:
-  - name: gitBranch
-    type: string
-    default: master
-  - name: azureJson
-    type: string
-    default: $(aro-v4-e2e-devops-spn)
-  - name: secretSA
-    type: string
-    default: rharosecretsdev
-  - name: location
-    type: string
-    default: eastus
-  - name: resourceGroup
-    type: string
-    default: $(AZURE_UNIQUE_PREFIX)-aro-${{ parameters.location }}
-  - name: databaseAccountName
-    type: string
-    default: $(AZURE_UNIQUE_PREFIX)-aro-${{ parameters.location }}
-  - name: keyvaultPrefix
-    type: string
-    default: $(AZURE_UNIQUE_PREFIX)-aro-${{ parameters.location }}
-  - name: dst_acr_name
-    type: string
-    default: $(AZURE_UNIQUE_PREFIX)aro
-  - name: aroImage
-    type: string
-    default: ${{ parameters.dst_acr_name }}.azurecr.io/aro:$(git rev-parse --short=7 HEAD) # do we have git?
-  - name: ocpVersionTag
-    type: string
-    default: latest
diff --git a/.pipelines/templates/rp-dev/rp-dev-vars.yml b/.pipelines/templates/rp-dev/rp-dev-vars.yml
deleted file mode 100644
index b9cf1efc6..000000000
--- a/.pipelines/templates/rp-dev/rp-dev-vars.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-variables:
-  AZURE_UNIQUE_PREFIX: zzz # dummy value
-  NO_CACHE: true
-  ARO_INSTALL_VIA_HIVE: true
-  ARO_ADOPT_BY_HIVE: true
-  DATABASE_NAME: ARO
diff --git a/.pipelines/templates/rp-dev/template-generate-rp-dev-config.yml b/.pipelines/templates/rp-dev/template-generate-rp-dev-config.yml
deleted file mode 100644
index fa2080fae..000000000
--- a/.pipelines/templates/rp-dev/template-generate-rp-dev-config.yml
+++ /dev/null
@@ -1,44 +0,0 @@
-parameters:
-  secretSA: ""
-  location: ""
-  resourceGroup: ""
-  databaseAccountName: ""
-  keyvaultPrefix: ""
-  aroImage: ""
-
-steps:
-  - script: |
-      set -e
-      # Export the SECRET_SA_ACCOUNT_NAME environment variable and run make secrets
-      export SECRET_SA_ACCOUNT_NAME=${{ parameters.secretSA }} && make secrets
-
-      # Define the expected directory and file names
-      expected_dir="secrets"
-      files=("env" "dev-ca.crt" "dev-client.crt")
-
-      # Validate that the secrets directory and required files exist
-      [ -d "$expected_dir" ] || { echo "Directory '$expected_dir' has not been created."; exit 1; }
-      for file in "${files[@]}"; do
-        [ -f "$expected_dir/$file" ] || { echo "File '$file' does not exist inside the directory '$expected_dir'."; exit 1; }
-      done
-    name: download-secrets
-    displayName: 🔑 Downloading certificates and secrets from storage account
-
-  - script: |
-      set -e
-      # Export environment variables from parameters
-      export LOCATION=${{ parameters.location }} RESOURCEGROUP=${{ parameters.resourceGroup }} DATABASE_ACCOUNT_NAME=${{ parameters.databaseAccountName }}
-      export KEYVAULT_PREFIX=${{ parameters.keyvaultPrefix }} ARO_IMAGE=${{ parameters.aroImage }}
-      # Source environment variables from the secrets file
-      source secrets/env
-
-      # Generate SSH key
-      ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa
-
-      # Run the make command to generate dev-config.yaml
-      make dev-config.yaml
-
-      # Check if the dev-config.yaml file exists
-      [ -f "dev-config.yaml" ] || { echo "File dev-config.yaml does not exist."; exit 1; }
-    name: generate-rp-dev-config
-    displayName: ⚙️ Generating RP development config yaml (dev-config.yaml)
diff --git a/.pipelines/templates/rp-dev/template-install-hive.yml b/.pipelines/templates/rp-dev/template-install-hive.yml
deleted file mode 100644
index 9bdb2d8e9..000000000
--- a/.pipelines/templates/rp-dev/template-install-hive.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-parameters:
-  location: ""
-
-steps:
-  - script: |
-      set -e
-      source ./hack/devtools/deploy-shared-env.sh
-      vpn_configuration
-      sudo openvpn secrets/vpn-${{ parameters.location }}.ovpn &
-      make aks.kubeconfig
-      ./hack/hive-generate-config.sh
-      KUBECONFIG=$(pwd)/aks.kubeconfig ./hack/hive-dev-install.sh
-    name: install-hive
-    displayName: 🚀 Installing Hive into AKS
diff --git a/.pipelines/templates/rp-dev/template-mirror-images_to_acr.yml b/.pipelines/templates/rp-dev/template-mirror-images_to_acr.yml
deleted file mode 100644
index 4e4d3ef0a..000000000
--- a/.pipelines/templates/rp-dev/template-mirror-images_to_acr.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-parameters:
-  mirrorTag: ""
-  dst_acr_name: ""
-steps:
-  - script: |
-      set -e
-      export SRC_AUTH_QUAY=$(echo $USER_PULL_SECRET | jq -r '.auths."quay.io".auth')
-      export SRC_AUTH_REDHAT=$(echo $USER_PULL_SECRET | jq -r '.auths."registry.redhat.io".auth')
-      export DST_AUTH=$(echo -n '00000000-0000-0000-0000-000000000000:'$(az acr login -n ${{ parameters.dst_acr_name }} --expose-token | jq -r .accessToken) | base64 -w0)
-      docker login -u 00000000-0000-0000-0000-000000000000 -p "$(echo $DST_AUTH | base64 -d | cut -d':' -f2)" "${{ parameters.dst_acr_name }}.azurecr.io"
-    name: acr-login
-    displayName: ⚙️ Login to ACR
-
-  - script: |
-      set -e
-      go run ./cmd/aro mirror ${{ parameters.mirrorTag }}
-
-      source ./hack/rp-dev-helper.sh
-    name: mirror-ocp-images-to-acr
-    displayName: ⚙️ Mirror OCP Images to ACR
-
-  - script: |
-      set -e
-      mdm_image_tag=$(get_digest_tag "MdmImage")
-      mdsd_image_tag=$(get_digest_tag "MdsdImage")
-
-      az acr import --name ${{ parameters.dst_acr_name }}.azurecr.io/${mdm_image_tag} --source linuxgeneva-microsoft.azurecr.io/${mdm_image_tag}
-      az acr import --name ${{ parameters.dst_acr_name }}.azurecr.io/${mdsd_image_tag} --source linuxgeneva-microsoft.azurecr.io/${mdsd_image_tag}
-    name: import-mdm-mdsd-images-to-acr
-    displayName: ⚙️ Import MDM and MDSD images to ACR
-
-  - script: make publish-image-aro-multistage
-    name: import-aro-image-to-acr
-    displayName: ⚙️ Build, push and import ARO image to ACR
-
-  - script: |
-      set -e
-      source ./hack/rp-dev-helper.sh
-      fluentbit_image_tag=$(get_digest_tag "FluentbitImage")
-      copy_digest_tag $PULL_SECRET, "arointsvc", ${{ parameters.dst_acr_name }}, $fluentbit_image_tag
-    name: build-push-aro-fluenbit-images-to-acr
-    displayName: ⚙️ Copy fluentbit image to ACR
diff --git a/.pipelines/templates/rp-dev/template-prepare-rp-deploy.yml b/.pipelines/templates/rp-dev/template-prepare-rp-deploy.yml
deleted file mode 100644
index 5fdf1e3ed..000000000
--- a/.pipelines/templates/rp-dev/template-prepare-rp-deploy.yml
+++ /dev/null
@@ -1,49 +0,0 @@
-parameters:
-    rpResourceGroup: ""
-    rpVmssName: ""
-    gwyResourceGroup: ""
-    gwyVmssName: ""
-
-steps:
-  - script: |
-      set -e
-      export PARENT_DOMAIN_NAME=osadev.cloud
-      export PARENT_DOMAIN_RESOURCEGROUP=dns
-      export GLOBAL_RESOURCEGROUP=${AZURE_PREFIX}-global
-
-      for DOMAIN_NAME in ${AZURE_PREFIX}-clusters.$PARENT_DOMAIN_NAME ${AZURE_PREFIX}-rp.$PARENT_DOMAIN_NAME; do
-          CHILD_DOMAIN_PREFIX="$(cut -d. -f1 <<<$DOMAIN_NAME)"
-          echo "########## Creating NS record to DNS Zone $CHILD_DOMAIN_PREFIX ##########"
-          az network dns record-set ns create \
-              --resource-group "$PARENT_DOMAIN_RESOURCEGROUP" \
-              --zone "$PARENT_DOMAIN_NAME" \
-              --name "$CHILD_DOMAIN_PREFIX" >/dev/null
-          for ns in $(az network dns zone show \
-              --resource-group "$GLOBAL_RESOURCEGROUP" \
-              --name "$DOMAIN_NAME" \
-              --query nameServers -o tsv); do
-              az network dns record-set ns add-record \
-              --resource-group "$PARENT_DOMAIN_RESOURCEGROUP" \
-              --zone "$PARENT_DOMAIN_NAME" \
-              --record-set-name "$CHILD_DOMAIN_PREFIX" \
-              --nsdname "$ns" >/dev/null
-          done
-      done
-    name: dns-domain-and-certificate-keyvault
-    displayName: ⚙️ Update the DNS Child Domains and the certificates in keyvault
-
-  - script: |
-      set -e
-      gitCommit=$(git rev-parse --short=7 HEAD)
-      az vmss delete -g ${{ parameters.rpResourceGroup }} --name ${{ parameters.rpVmssName }}-$gitCommit  --force-deletion
-    name: delete-rp-vmss
-    displayName: ⚙️ Delete RP VMSS
-    continueOnError: false
-
-  - script: |
-      set -e
-      gitCommit=$(git rev-parse --short=7 HEAD)
-      az vmss delete -g ${{ parameters.gwyResourceGroup }} --name ${{ parameters.gwyVmssName }}-$gitCommit  --force-deletion
-    name: delete-gwy-vmss
-    displayName: ⚙️ Delete GWY VMSS
-    continueOnError: false
diff --git a/Dockerfile.ci-rp b/Dockerfile.ci-rp
index b4bbcc0d1..f6a06c708 100644
--- a/Dockerfile.ci-rp
+++ b/Dockerfile.ci-rp
@@ -56,7 +56,7 @@ RUN go build -ldflags "-X github.com/Azure/ARO-RP/pkg/util/version.GitCommit=${A
 RUN go test ./test/e2e/... -tags e2e,codec.safe -c -ldflags "-X github.com/Azure/ARO-RP/pkg/util/version.GitCommit=${ARO_VERSION}" -o e2e.test
 
 # Additional tests
-RUN go run gotest.tools/gotestsum@v1.11.0 --format pkgname --junitfile report.xml -- -coverprofile=cover.out ./...
+RUN ARO_SKIP_PKI_TESTS=true go run gotest.tools/gotestsum@v1.11.0 --format pkgname --junitfile report.xml -- -coverprofile=cover.out ./...
 RUN hack/fips/validate-fips.sh ./aro
 
 ###############################################################################
diff --git a/Makefile b/Makefile
index 8e4b7676a..57608b2fd 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,6 @@ TAG ?= $(shell git describe --exact-match 2>/dev/null)
 COMMIT = $(shell git rev-parse --short=7 HEAD)$(shell [[ $$(git status --porcelain) = "" ]] || echo -dirty)
 ARO_IMAGE_BASE = ${RP_IMAGE_ACR}.azurecr.io/aro
 E2E_FLAGS ?= -test.v --ginkgo.v --ginkgo.timeout 180m --ginkgo.flake-attempts=2 --ginkgo.junit-report=e2e-report.xml
-E2E_LABEL ?= !smoke
 GO_FLAGS ?= -tags=containers_image_openpgp,exclude_graphdriver_btrfs,exclude_graphdriver_devicemapper
 NO_CACHE ?= true
 
@@ -269,7 +268,7 @@ e2etools:
 
 .PHONY: test-e2e
 test-e2e: e2e.test
-	./e2e.test $(E2E_FLAGS) --ginkgo.label-filter="$(E2E_LABEL)"
+	./e2e.test $(E2E_FLAGS)
 
 .PHONY: test-go
 test-go: generate build-all validate-go lint-go unit-test-go
diff --git a/docs/deploy-development-rp.md b/docs/deploy-development-rp.md
index edad0dcdd..e8be02f66 100644
--- a/docs/deploy-development-rp.md
+++ b/docs/deploy-development-rp.md
@@ -119,6 +119,9 @@
    1. use the create utility:
 
       ```bash
+      # Create the application to run the cluster as and load it
+      CLUSTER=<cluster-name> go run ./hack/cluster createapp
+      source clusterapp.env
       # Create the cluster
       CLUSTER=<cluster-name> go run ./hack/cluster create
       ```
@@ -127,6 +130,7 @@
    
       ```bash
       CLUSTER=<cluster-name> go run ./hack/cluster delete
+      CLUSTER=<cluster-name> go run ./hack/cluster deleteapp
       ```
 
       By default, a public cluster will be created. In order to create a private cluster, set the `PRIVATE_CLUSTER` environment variable to `true` prior to creation. Internet access from the cluster can also be restricted by setting the `NO_INTERNET` environment variable to `true`.
diff --git a/env.example b/env.example
index a265550d1..2f420e764 100644
--- a/env.example
+++ b/env.example
@@ -3,4 +3,6 @@ export ARO_IMAGE=arointsvc.azurecr.io/aro:latest
 export NO_CACHE=false
 export AZURE_EXTENSION_DEV_SOURCES="$(pwd)/python"
 
+export ARO_SKIP_PKI_TESTS=true
+
 . secrets/env
diff --git a/hack/cluster/cluster.go b/hack/cluster/cluster.go
index 8fab0aa9a..fd822d32d 100644
--- a/hack/cluster/cluster.go
+++ b/hack/cluster/cluster.go
@@ -25,7 +25,7 @@ const (
 
 func run(ctx context.Context, log *logrus.Entry) error {
 	if len(os.Args) != 2 {
-		return fmt.Errorf("usage: CLUSTER=x %s {create,delete}", os.Args[0])
+		return fmt.Errorf("usage: CLUSTER=x %s {create,createApp,deleteApp,delete}", os.Args[0])
 	}
 
 	if err := env.ValidateVars(Cluster); err != nil {
@@ -59,6 +59,10 @@ func run(ctx context.Context, log *logrus.Entry) error {
 	switch strings.ToLower(os.Args[1]) {
 	case "create":
 		return c.Create(ctx, vnetResourceGroup, clusterName, osClusterVersion)
+	case "createapp":
+		return c.CreateApp(ctx, clusterName)
+	case "deleteapp":
+		return c.DeleteApp(ctx)
 	case "delete":
 		return c.Delete(ctx, vnetResourceGroup, clusterName)
 	default:
diff --git a/hack/devtools/rp-dev-helper.sh b/hack/devtools/rp-dev-helper.sh
deleted file mode 100755
index 43e1e98ee..000000000
--- a/hack/devtools/rp-dev-helper.sh
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/bash -e
-######## Helper file to run full RP dev either locally or using Azure DevOps Pipelines ########
-
-# Function to extract the image tag - (FUNCTION_NAME)
-extract_image_tag() {
-    # Extract the line containing the return statement
-    local return_line=$(grep -A 1 "func $1" "$2" | grep 'return')
-    echo "$return_line" | sed 's/.*"\(.*\)@sha256.*/\1/'
-}
-
-# Function to copy image using skopeo -(PULL_SECRET, SRC_ACR_NAME, DST_ACR_NAME, IMAGE_TAG)
-copy_digest_tag() {
-    local PULL_SECRET=$1
-    local SRC_ACR_NAME=$2
-    local DST_ACR_NAME=$3
-    local IMAGE_TAG=$4
-    
-    SRC_AUTH=$(echo "$PULL_SECRET" | jq -r '.auths["'$SRC_ACR_NAME'.azurecr.io"].auth' | base64 -d)
-    DST_TOKEN=$(az acr login -n "$DST_ACR_NAME" --expose-token | jq -r .accessToken)
-    
-    skopeo copy \
-        --src-creds "$SRC_AUTH" \
-        --dest-creds "00000000-0000-0000-0000-000000000000:$DST_TOKEN" \
-        "docker://$SRC_ACR_NAME.azurecr.io$IMAGE_TAG" \
-        "docker://$DST_ACR_NAME.azurecr.io$IMAGE_TAG"
-}
-
-VERSION_CONST_FILE="pkg/util/version/const.go"
-# Function to get image name and tag
-get_digest_tag() {
-    local IMAGE_NAME=$1
-    local IMAGE_TAG=$(extract_image_tag "$IMAGE_NAME" "$VERSION_CONST_FILE")
-    echo "$IMAGE_NAME and Tag: $IMAGE_TAG"
-    echo "$IMAGE_TAG"
-}
-
-# Example usage
-# get_digest_tag "FluentbitImage"
-# copy_digest_tag "<PULL_SECRET>" "src_acr_name" "dst_acr_name" "$(get_digest_tag FluentbitImage)"
diff --git a/pkg/api/correlation.go b/pkg/api/correlation.go
index 14d2a94f5..6cf55d109 100644
--- a/pkg/api/correlation.go
+++ b/pkg/api/correlation.go
@@ -1,24 +1,15 @@
 package api
 
 import (
-	"context"
-	"net/http"
 	"time"
-
-	"github.com/Azure/ARO-RP/pkg/util/uuid"
 )
 
 // Copyright (c) Microsoft Corporation.
 // Licensed under the Apache License 2.0.
 
-type contextKey int
-
-const (
-	contextKeyCorrelationData contextKey = iota
-)
-
-// CorrelationData represents data used for metrics or tracing between ARO-RP and ARM.
-// https://eng.ms/docs/products/arm/rpaas/contract/requestheaders
+// CorrelationData represents any data, used for metrics or tracing.
+// More on these values:
+// https://github.com/Azure/azure-resource-manager-rpc/blob/master/v1.0/common-api-details.md
 type CorrelationData struct {
 	// CorrelationID contains value of x-ms-correlation-request-id
 	CorrelationID string `json:"correlationId,omitempty"`
@@ -26,9 +17,6 @@ type CorrelationData struct {
 	// ClientRequestID contains value of x-ms-client-request-id
 	ClientRequestID string `json:"clientRequestId,omitempty"`
 
-	// OperationID contains the unique ID generated for each operation that the ARO-RP performs
-	OperationID string `json:"operationID,omitempty"`
-
 	// RequestID contains value of x-ms-request-id
 	RequestID string `json:"requestID,omitempty"`
 
@@ -38,28 +26,3 @@ type CorrelationData struct {
 	// RequestTime is the time that the request was received
 	RequestTime time.Time `json:"requestTime,omitempty"`
 }
-
-func CtxWithCorrelationData(ctx context.Context, correlationData *CorrelationData) context.Context {
-	return context.WithValue(ctx, contextKeyCorrelationData, correlationData)
-}
-
-func GetCorrelationDataFromCtx(ctx context.Context) *CorrelationData {
-	correlationData, ok := ctx.Value(contextKeyCorrelationData).(*CorrelationData)
-	if !ok {
-		return nil
-	}
-	return correlationData
-}
-
-func CreateCorrelationDataFromReq(req *http.Request) *CorrelationData {
-	if req == nil {
-		return nil
-	}
-
-	return &CorrelationData{
-		ClientRequestID: req.Header.Get("X-Ms-Client-Request-Id"),
-		CorrelationID:   req.Header.Get("X-Ms-Correlation-Request-Id"),
-		RequestID:       uuid.DefaultGenerator.Generate(),
-		OperationID:     uuid.DefaultGenerator.Generate(),
-	}
-}
diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go
index f1dd2e124..23b194df3 100644
--- a/pkg/cluster/cluster.go
+++ b/pkg/cluster/cluster.go
@@ -5,7 +5,6 @@ package cluster
 
 import (
 	"context"
-	"net/http"
 	"time"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
@@ -34,7 +33,6 @@ import (
 	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned"
 	"github.com/Azure/ARO-RP/pkg/operator/deploy"
 	"github.com/Azure/ARO-RP/pkg/util/azblob"
-	"github.com/Azure/ARO-RP/pkg/util/azureclient"
 	"github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/armnetwork"
 	"github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/common"
 	"github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/authorization"
@@ -182,14 +180,10 @@ func New(ctx context.Context, log *logrus.Entry, _env env.Interface, db database
 		return nil, err
 	}
 
-	customRoundTripper := azureclient.NewCustomRoundTripper(http.DefaultTransport)
 	clientOptions := arm.ClientOptions{
 		ClientOptions: azcore.ClientOptions{
 			Cloud: _env.Environment().Cloud,
 			Retry: common.RetryOptions,
-			Transport: &http.Client{
-				Transport: customRoundTripper,
-			},
 		},
 	}
 
diff --git a/pkg/cluster/delete.go b/pkg/cluster/delete.go
index 16a37073a..3796a7cb5 100644
--- a/pkg/cluster/delete.go
+++ b/pkg/cluster/delete.go
@@ -26,7 +26,6 @@ import (
 	"github.com/Azure/ARO-RP/pkg/util/azureclient"
 	"github.com/Azure/ARO-RP/pkg/util/azureerrors"
 	"github.com/Azure/ARO-RP/pkg/util/dns"
-	utilnet "github.com/Azure/ARO-RP/pkg/util/net"
 	"github.com/Azure/ARO-RP/pkg/util/oidcbuilder"
 	"github.com/Azure/ARO-RP/pkg/util/rbac"
 	"github.com/Azure/ARO-RP/pkg/util/stringutils"
@@ -64,6 +63,27 @@ func (m *manager) deleteNic(ctx context.Context, nicName string) error {
 	return m.interfaces.DeleteAndWait(ctx, resourceGroup, *nic.Name)
 }
 
+func (m *manager) deletePrivateDNSVirtualNetworkLinks(ctx context.Context, resourceID string) error {
+	r, err := azure.ParseResourceID(resourceID)
+	if err != nil {
+		return err
+	}
+
+	virtualNetworkLinks, err := m.virtualNetworkLinks.List(ctx, r.ResourceGroup, r.ResourceName, nil)
+	if err != nil {
+		return err
+	}
+
+	for _, virtualNetworkLink := range virtualNetworkLinks {
+		err = m.virtualNetworkLinks.DeleteAndWait(ctx, r.ResourceGroup, r.ResourceName, *virtualNetworkLink.Name, "")
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
 func (m *manager) disconnectSecurityGroup(ctx context.Context, resourceID string) error {
 	r, err := azure.ParseResourceID(resourceID)
 	if err != nil {
@@ -204,7 +224,7 @@ func (m *manager) deleteResources(ctx context.Context) error {
 
 			case "microsoft.network/privatednszones":
 				m.log.Printf("deleting private DNS nested resources of %s", *resource.ID)
-				err = utilnet.DeletePrivateDNSVNetLinks(ctx, m.virtualNetworkLinks, *resource.ID)
+				err = m.deletePrivateDNSVirtualNetworkLinks(ctx, *resource.ID)
 				if err != nil {
 					return err
 				}
diff --git a/pkg/cluster/removeprivatednszone.go b/pkg/cluster/removeprivatednszone.go
index 99d632d7d..cd3a8d36b 100644
--- a/pkg/cluster/removeprivatednszone.go
+++ b/pkg/cluster/removeprivatednszone.go
@@ -5,21 +5,147 @@ package cluster
 
 import (
 	"context"
+	"strings"
 
-	utilnet "github.com/Azure/ARO-RP/pkg/util/net"
+	"github.com/Azure/go-autorest/autorest/azure"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/client-go/util/retry"
+
+	"github.com/Azure/ARO-RP/pkg/util/ready"
+	"github.com/Azure/ARO-RP/pkg/util/stringutils"
+	"github.com/Azure/ARO-RP/pkg/util/version"
 )
 
 func (m *manager) removePrivateDNSZone(ctx context.Context) error {
-	resourceGroupID := m.doc.OpenShiftCluster.Properties.ClusterProfile.ResourceGroupID
-	config := utilnet.PrivateZoneRemovalConfig{
-		Log:                m.log,
-		PrivateZonesClient: m.privateZones,
-		Configcli:          m.configcli,
-		Mcocli:             m.mcocli,
-		Kubernetescli:      m.kubernetescli,
-		VNetLinksClient:    m.virtualNetworkLinks,
-		ResourceGroupID:    resourceGroupID,
-	}
-
-	return utilnet.RemovePrivateDNSZone(ctx, config)
+	resourceGroup := stringutils.LastTokenByte(m.doc.OpenShiftCluster.Properties.ClusterProfile.ResourceGroupID, '/')
+
+	zones, err := m.privateZones.ListByResourceGroup(ctx, resourceGroup, nil)
+	if err != nil {
+		m.log.Print(err)
+		return nil
+	}
+
+	if len(zones) == 0 {
+		// fix up any clusters that we already upgraded
+		err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
+			dns, err := m.configcli.ConfigV1().DNSes().Get(ctx, "cluster", metav1.GetOptions{})
+			if err != nil {
+				return err
+			}
+
+			if dns.Spec.PrivateZone == nil ||
+				!strings.HasPrefix(strings.ToLower(dns.Spec.PrivateZone.ID), strings.ToLower(m.doc.OpenShiftCluster.Properties.ClusterProfile.ResourceGroupID)) {
+				return nil
+			}
+
+			dns.Spec.PrivateZone = nil
+
+			_, err = m.configcli.ConfigV1().DNSes().Update(ctx, dns, metav1.UpdateOptions{})
+			return err
+		})
+		if err != nil {
+			m.log.Print(err)
+		}
+
+		return nil
+	}
+
+	mcps, err := m.mcocli.MachineconfigurationV1().MachineConfigPools().List(ctx, metav1.ListOptions{})
+	if err != nil {
+		m.log.Print(err)
+		return nil
+	}
+
+	var machineCount int
+	for _, mcp := range mcps.Items {
+		var found bool
+		for _, source := range mcp.Status.Configuration.Source {
+			if source.Name == "99-"+mcp.Name+"-aro-dns" {
+				found = true
+				break
+			}
+		}
+
+		if !found {
+			m.log.Printf("ARO DNS config not found in MCP %s", mcp.Name)
+			return nil
+		}
+
+		if !ready.MachineConfigPoolIsReady(&mcp) {
+			m.log.Printf("MCP %s not ready", mcp.Name)
+			return nil
+		}
+
+		machineCount += int(mcp.Status.MachineCount)
+	}
+
+	nodes, err := m.kubernetescli.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
+	if err != nil {
+		m.log.Print(err)
+		return nil
+	}
+
+	if len(nodes.Items) != machineCount {
+		m.log.Printf("cluster has %d nodes but %d under MCPs, not removing private DNS zone", len(nodes.Items), machineCount)
+		return nil
+	}
+
+	cv, err := m.configcli.ConfigV1().ClusterVersions().Get(ctx, "version", metav1.GetOptions{})
+	if err != nil {
+		return err
+	}
+	v, err := version.GetClusterVersion(cv)
+	if err != nil {
+		m.log.Print(err)
+		return nil
+	}
+
+	if v.Lt(version.NewVersion(4, 4)) {
+		// 4.3 uses SRV records for etcd
+		m.log.Printf("cluster version < 4.4, not removing private DNS zone")
+		return nil
+	}
+
+	err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
+		dns, err := m.configcli.ConfigV1().DNSes().Get(ctx, "cluster", metav1.GetOptions{})
+		if err != nil {
+			return err
+		}
+
+		if dns.Spec.PrivateZone == nil ||
+			!strings.HasPrefix(strings.ToLower(dns.Spec.PrivateZone.ID), strings.ToLower(m.doc.OpenShiftCluster.Properties.ClusterProfile.ResourceGroupID)) {
+			return nil
+		}
+
+		dns.Spec.PrivateZone = nil
+
+		_, err = m.configcli.ConfigV1().DNSes().Update(ctx, dns, metav1.UpdateOptions{})
+		return err
+	})
+	if err != nil {
+		m.log.Print(err)
+		return nil
+	}
+
+	for _, zone := range zones {
+		err = m.deletePrivateDNSVirtualNetworkLinks(ctx, *zone.ID)
+		if err != nil {
+			m.log.Print(err)
+			return nil
+		}
+
+		r, err := azure.ParseResourceID(*zone.ID)
+		if err != nil {
+			m.log.Print(err)
+			return nil
+		}
+
+		err = m.privateZones.DeleteAndWait(ctx, resourceGroup, r.ResourceName, "")
+		if err != nil {
+			m.log.Print(err)
+			return nil
+		}
+	}
+
+	return nil
 }
diff --git a/pkg/deploy/assets/rp-production.json b/pkg/deploy/assets/rp-production.json
index a43f7c4ac..4cb4aa2f8 100644
--- a/pkg/deploy/assets/rp-production.json
+++ b/pkg/deploy/assets/rp-production.json
@@ -443,7 +443,7 @@
                                     "autoUpgradeMinorVersion": true,
                                     "settings": {},
                                     "protectedSettings": {
-                                        "script": "[base64(concat(base64ToString('c2V0IC1leAoK'),'ACRRESOURCEID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('acrResourceId')),''')\n','ADMINAPICLIENTCERTCOMMONNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('adminApiClientCertCommonName')),''')\n','ARMAPICLIENTCERTCOMMONNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('armApiClientCertCommonName')),''')\n','ARMCLIENTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('armClientId')),''')\n','AZURECLOUDNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureCloudName')),''')\n','AZURESECPACKQUALYSURL=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureSecPackQualysUrl')),''')\n','AZURESECPACKVSATENANTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureSecPackVSATenantId')),''')\n','CLUSTERMDMACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterMdmAccount')),''')\n','CLUSTERMDSDACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterMdsdAccount')),''')\n','CLUSTERMDSDCONFIGVERSION=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterMdsdConfigVersion')),''')\n','CLUSTERMDSDNAMESPACE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterMdsdNamespace')),''')\n','CLUSTERPARENTDOMAINNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterParentDomainName')),''')\n','DATABASEACCOUNTNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('databaseAccountName')),''')\n','FLUENTBITIMAGE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('fluentbitImage')),''')\n','FPCLIENTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('fpClientId')),''')\n','FPSERVICEPRINCIPALID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('fpServicePrincipalId')),''')\n','GATEWAYDOMAINS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayDomains')),''')\n','GATEWAYRESOURCEGROUPNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayResourceGroupName')),''')\n','GATEWAYSERVICEPRINCIPALID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayServicePrincipalId')),''')\n','KEYVAULTDNSSUFFIX=$(base64 -d \u003c\u003c\u003c''',base64(parameters('keyvaultDNSSuffix')),''')\n','KEYVAULTPREFIX=$(base64 -d \u003c\u003c\u003c''',base64(parameters('keyvaultPrefix')),''')\n','MDMFRONTENDURL=$(base64 -d \u003c\u003c\u003c''',base64(parameters('mdmFrontendUrl')),''')\n','MDSDENVIRONMENT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('mdsdEnvironment')),''')\n','PORTALACCESSGROUPIDS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('portalAccessGroupIds')),''')\n','PORTALCLIENTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('portalClientId')),''')\n','PORTALELEVATEDGROUPIDS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('portalElevatedGroupIds')),''')\n','RPFEATURES=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpFeatures')),''')\n','RPIMAGE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpImage')),''')\n','RPMDMACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdmAccount')),''')\n','RPMDSDACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdsdAccount')),''')\n','RPMDSDCONFIGVERSION=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdsdConfigVersion')),''')\n','RPMDSDNAMESPACE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdsdNamespace')),''')\n','RPPARENTDOMAINNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpParentDomainName')),''')\n','OIDCSTORAGEACCOUNTNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('oidcStorageAccountName')),''')\n','CLUSTERSINSTALLVIAHIVE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clustersInstallViaHive')),''')\n','CLUSTERSADOPTBYHIVE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clustersAdoptByHive')),''')\n','CLUSTERDEFAULTINSTALLERPULLSPEC=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterDefaultInstallerPullspec')),''')\n','USECHECKACCESS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('useCheckAccess')),''')\n','ADMINAPICABUNDLE=''',parameters('adminApiCaBundle'),'''\n','ARMAPICABUNDLE=''',parameters('armApiCaBundle'),'''\n','MDMIMAGE=''/distroless/genevamdm:2.2024.517.533-b73893-20240522t0954@sha256:939df9d7b6660874697f8ebed1fe56504f86d92f99801a9dc6fd98e9176d3f75''\n','LOCATION=$(base64 -d \u003c\u003c\u003c''',base64(resourceGroup().location),''')\n','SUBSCRIPTIONID=$(base64 -d \u003c\u003c\u003c''',base64(subscription().subscriptionId),''')\n','RESOURCEGROUPNAME=$(base64 -d \u003c\u003c\u003c''',base64(resourceGroup().name),''')\n','\n',base64ToString('IyEvYmluL2Jhc2gKCmVjaG8gInNldHRpbmcgc3NoIHBhc3N3b3JkIGF1dGhlbnRpY2F0aW9uIgojIFdlIG5lZWQgdG8gbWFudWFsbHkgc2V0IFBhc3N3b3JkQXV0aGVudGljYXRpb24gdG8gdHJ1ZSBpbiBvcmRlciBmb3IgdGhlIFZNU1MgQWNjZXNzIEpJVCB0byB3b3JrCnNlZCAtaSAncy9QYXNzd29yZEF1dGhlbnRpY2F0aW9uIG5vL1Bhc3N3b3JkQXV0aGVudGljYXRpb24geWVzL2cnIC9ldGMvc3NoL3NzaGRfY29uZmlnCnN5c3RlbWN0bCByZWxvYWQgc3NoZC5zZXJ2aWNlCgojQWRkaW5nIHJldHJ5IGxvZ2ljIHRvIHl1bSBjb21tYW5kcyBpbiBvcmRlciB0byBhdm9pZCBzdGFsbGluZyBvdXQgb24gcmVzb3VyY2UgbG9ja3MKZWNobyAicnVubmluZyBSSFVJIGZpeCIKZm9yIGF0dGVtcHQgaW4gezEuLjYwfTsgZG8KICB5dW0gdXBkYXRlIC15IC0tZGlzYWJsZXJlcG89JyonIC0tZW5hYmxlcmVwbz0ncmh1aS1taWNyb3NvZnQtYXp1cmUqJyAmJiBicmVhawogIGlmIFtbICR7YXR0ZW1wdH0gLWx0IDYwIF1dOyB0aGVuIHNsZWVwIDMwOyBlbHNlIGV4aXQgMTsgZmkKZG9uZQoKZWNobyAicnVubmluZyB5dW0gdXBkYXRlIgpmb3IgYXR0ZW1wdCBpbiB7MS4uNjB9OyBkbwogIHl1bSAteSAteCBXQUxpbnV4QWdlbnQgLXggV0FMaW51eEFnZW50LXVkZXYgdXBkYXRlIC0tYWxsb3dlcmFzaW5nICYmIGJyZWFrCiAgaWYgW1sgJHthdHRlbXB0fSAtbHQgNjAgXV07IHRoZW4gc2xlZXAgMzA7IGVsc2UgZXhpdCAxOyBmaQpkb25lCgplY2hvICJleHRlbmRpbmcgcGFydGl0aW9uIHRhYmxlIgojIExpbnV4IGJsb2NrIGRldmljZXMgYXJlIGluY29uc2lzdGVudGx5IG5hbWVkCiMgaXQncyBkaWZmaWN1bHQgdG8gdGllIHRoZSBsdm0gcHYgdG8gdGhlIHBoeXNpY2FsIGRpc2sgdXNpbmcgL2Rldi9kaXNrIGZpbGVzLCB3aGljaCBpcyB3aHkgbHZzIGlzIHVzZWQgaGVyZQpwaHlzaWNhbERpc2s9IiQobHZzIC1vIGRldmljZXMgLWEgfCBoZWFkIC1uMiB8IHRhaWwgLW4xIHwgY3V0IC1kICcgJyAtZiAzIHwgY3V0IC1kIFwoIC1mIDEgfCB0ciAtZCAnWzpkaWdpdDpdJykiCmdyb3dwYXJ0ICIkcGh5c2ljYWxEaXNrIiAyCgplY2hvICJleHRlbmRpbmcgZmlsZXN5c3RlbXMiCmx2ZXh0ZW5kIC1sICsyMCVGUkVFIC9kZXYvcm9vdHZnL3Jvb3Rsdgp4ZnNfZ3Jvd2ZzIC8KCmx2ZXh0ZW5kIC1sICsxMDAlRlJFRSAvZGV2L3Jvb3R2Zy92YXJsdgp4ZnNfZ3Jvd2ZzIC92YXIKCmVjaG8gImltcG9ydGluZyBycG0gcmVwb3NpdG9yaWVzIgpycG0gLS1pbXBvcnQgaHR0cHM6Ly9kbC5mZWRvcmFwcm9qZWN0Lm9yZy9wdWIvZXBlbC9SUE0tR1BHLUtFWS1FUEVMLTgKcnBtIC0taW1wb3J0IGh0dHBzOi8vcGFja2FnZXMubWljcm9zb2Z0LmNvbS9rZXlzL21pY3Jvc29mdC5hc2MKCmZvciBhdHRlbXB0IGluIHsxLi42MH07IGRvCiAgeXVtIC15IGluc3RhbGwgaHR0cHM6Ly9kbC5mZWRvcmFwcm9qZWN0Lm9yZy9wdWIvZXBlbC9lcGVsLXJlbGVhc2UtbGF0ZXN0LTgubm9hcmNoLnJwbSAmJiBicmVhawogIGlmIFtbICR7YXR0ZW1wdH0gLWx0IDYwIF1dOyB0aGVuIHNsZWVwIDMwOyBlbHNlIGV4aXQgMTsgZmkKZG9uZQoKZWNobyAiY29uZmlndXJpbmcgbG9ncm90YXRlIgpjYXQgPi9ldGMvbG9ncm90YXRlLmNvbmYgPDwnRU9GJwojIHNlZSAibWFuIGxvZ3JvdGF0ZSIgZm9yIGRldGFpbHMKIyByb3RhdGUgbG9nIGZpbGVzIHdlZWtseQp3ZWVrbHkKCiMga2VlcCAyIHdlZWtzIHdvcnRoIG9mIGJhY2tsb2dzCnJvdGF0ZSAyCgojIGNyZWF0ZSBuZXcgKGVtcHR5KSBsb2cgZmlsZXMgYWZ0ZXIgcm90YXRpbmcgb2xkIG9uZXMKY3JlYXRlCgojIHVzZSBkYXRlIGFzIGEgc3VmZml4IG9mIHRoZSByb3RhdGVkIGZpbGUKZGF0ZWV4dAoKIyB1bmNvbW1lbnQgdGhpcyBpZiB5b3Ugd2FudCB5b3VyIGxvZyBmaWxlcyBjb21wcmVzc2VkCmNvbXByZXNzCgojIFJQTSBwYWNrYWdlcyBkcm9wIGxvZyByb3RhdGlvbiBpbmZvcm1hdGlvbiBpbnRvIHRoaXMgZGlyZWN0b3J5CmluY2x1ZGUgL2V0Yy9sb2dyb3RhdGUuZAoKIyBubyBwYWNrYWdlcyBvd24gd3RtcCBhbmQgYnRtcCAtLSB3ZSdsbCByb3RhdGUgdGhlbSBoZXJlCi92YXIvbG9nL3d0bXAgewogICAgbW9udGhseQogICAgY3JlYXRlIDA2NjQgcm9vdCB1dG1wCiAgICAgICAgbWluc2l6ZSAxTQogICAgcm90YXRlIDEKfQoKL3Zhci9sb2cvYnRtcCB7CiAgICBtaXNzaW5nb2sKICAgIG1vbnRobHkKICAgIGNyZWF0ZSAwNjAwIHJvb3QgdXRtcAogICAgcm90YXRlIDEKfQpFT0YKCmVjaG8gImNvbmZpZ3VyaW5nIHl1bSByZXBvc2l0b3J5IGFuZCBydW5uaW5nIHl1bSB1cGRhdGUiCmNhdCA+L2V0Yy95dW0ucmVwb3MuZC9henVyZS5yZXBvIDw8J0VPRicKW2F6dXJlLWNsaV0KbmFtZT1henVyZS1jbGkKYmFzZXVybD1odHRwczovL3BhY2thZ2VzLm1pY3Jvc29mdC5jb20veXVtcmVwb3MvYXp1cmUtY2xpCmVuYWJsZWQ9eWVzCmdwZ2NoZWNrPXllcwoKW2F6dXJlY29yZV0KbmFtZT1henVyZWNvcmUKYmFzZXVybD1odHRwczovL3BhY2thZ2VzLm1pY3Jvc29mdC5jb20veXVtcmVwb3MvYXp1cmVjb3JlCmVuYWJsZWQ9eWVzCmdwZ2NoZWNrPW5vCkVPRgoKc2VtYW5hZ2UgZmNvbnRleHQgLWEgLXQgdmFyX2xvZ190ICIvdmFyL2xvZy9qb3VybmFsKC8uKik/Igpta2RpciAtcCAvdmFyL2xvZy9qb3VybmFsCgpmb3IgYXR0ZW1wdCBpbiB7MS4uNjB9OyBkbwp5dW0gLXkgaW5zdGFsbCBjbGFtYXYgYXpzZWMtY2xhbWF2IGF6c2VjLW1vbml0b3IgYXp1cmUtY2xpIGF6dXJlLW1kc2QgYXp1cmUtc2VjdXJpdHkgcG9kbWFuIHBvZG1hbi1kb2NrZXIgb3BlbnNzbC1wZXJsIHB5dGhvbjMgJiYgYnJlYWsKICAjIGhhY2sgLSB3ZSBhcmUgaW5zdGFsbGluZyBweXRob24zIG9uIGhvc3RzIGR1ZSB0byBhbiBpc3N1ZSB3aXRoIEF6dXJlIExpbnV4IEV4dGVuc2lvbnMgaHR0cHM6Ly9naXRodWIuY29tL0F6dXJlL2F6dXJlLWxpbnV4LWV4dGVuc2lvbnMvcHVsbC8xNTA1CiAgaWYgW1sgJHthdHRlbXB0fSAtbHQgNjAgXV07IHRoZW4gc2xlZXAgMzA7IGVsc2UgZXhpdCAxOyBmaQpkb25lCgojIGh0dHBzOi8vYWNjZXNzLnJlZGhhdC5jb20vc2VjdXJpdHkvY3ZlL2N2ZS0yMDIwLTEzNDAxCmVjaG8gImFwcGx5aW5nIGZpcmV3YWxsIHJ1bGVzIgpjYXQgPi9ldGMvc3lzY3RsLmQvMDItZGlzYWJsZS1hY2NlcHQtcmEuY29uZiA8PCdFT0YnCm5ldC5pcHY2LmNvbmYuYWxsLmFjY2VwdF9yYT0wCkVPRgoKY2F0ID4vZXRjL3N5c2N0bC5kLzAxLWRpc2FibGUtY29yZS5jb25mIDw8J0VPRicKa2VybmVsLmNvcmVfcGF0dGVybiA9IHwvYmluL3RydWUKRU9GCnN5c2N0bCAtLXN5c3RlbQoKZmlyZXdhbGwtY21kIC0tYWRkLXBvcnQ9NDQzL3RjcCAtLXBlcm1hbmVudApmaXJld2FsbC1jbWQgLS1hZGQtcG9ydD00NDQvdGNwIC0tcGVybWFuZW50CmZpcmV3YWxsLWNtZCAtLWFkZC1wb3J0PTIyMjIvdGNwIC0tcGVybWFuZW50CgpleHBvcnQgQVpVUkVfQ0xPVURfTkFNRT0kQVpVUkVDTE9VRE5BTUUKCmVjaG8gImxvZ2dpbmcgaW50byBwcm9kIGFjciIKYXogbG9naW4gLWkgLS1hbGxvdy1uby1zdWJzY3JpcHRpb25zCgojIFN1cHByZXNzIGVtdWxhdGlvbiBvdXRwdXQgZm9yIHBvZG1hbiBpbnN0ZWFkIG9mIGRvY2tlciBmb3IgYXogYWNyIGNvbXBhdGFiaWxpdHkKbWtkaXIgLXAgL2V0Yy9jb250YWluZXJzLwp0b3VjaCAvZXRjL2NvbnRhaW5lcnMvbm9kb2NrZXIKCm1rZGlyIC1wIC9yb290Ly5kb2NrZXIKUkVHSVNUUllfQVVUSF9GSUxFPS9yb290Ly5kb2NrZXIvY29uZmlnLmpzb24gYXogYWNyIGxvZ2luIC0tbmFtZSAiJChzZWQgLWUgJ3N8LiovfHwnIDw8PCIkQUNSUkVTT1VSQ0VJRCIpIgoKTURNSU1BR0U9IiR7UlBJTUFHRSUlLyp9LyR7TURNSU1BR0UjKi99Igpkb2NrZXIgcHVsbCAiJE1ETUlNQUdFIgpkb2NrZXIgcHVsbCAiJFJQSU1BR0UiCmRvY2tlciBwdWxsICIkRkxVRU5UQklUSU1BR0UiCgpheiBsb2dvdXQKCmVjaG8gImNvbmZpZ3VyaW5nIGZsdWVudGJpdCBzZXJ2aWNlIgpta2RpciAtcCAvZXRjL2ZsdWVudGJpdC8KbWtkaXIgLXAgL3Zhci9saWIvZmx1ZW50CgpjYXQgPi9ldGMvZmx1ZW50Yml0L2ZsdWVudGJpdC5jb25mIDw8J0VPRicKW0lOUFVUXQoJTmFtZSBzeXN0ZW1kCglUYWcgam91cm5hbGQKCVN5c3RlbWRfRmlsdGVyIF9DT01NPWFybwoJREIgL3Zhci9saWIvZmx1ZW50L2pvdXJuYWxkYgoKW0ZJTFRFUl0KCU5hbWUgbW9kaWZ5CglNYXRjaCBqb3VybmFsZAoJUmVtb3ZlX3dpbGRjYXJkIF8KCVJlbW92ZSBUSU1FU1RBTVAKCltGSUxURVJdCglOYW1lIHJld3JpdGVfdGFnCglNYXRjaCBqb3VybmFsZAoJUnVsZSAkTE9HS0lORCBhc3luY3FvcyBhc3luY3FvcyB0cnVlCgpbRklMVEVSXQoJTmFtZSBtb2RpZnkKCU1hdGNoIGFzeW5jcW9zCglSZW1vdmUgQ0xJRU5UX1BSSU5DSVBBTF9OQU1FCglSZW1vdmUgRklMRQoJUmVtb3ZlIENPTVBPTkVOVAoKW0ZJTFRFUl0KCU5hbWUgcmV3cml0ZV90YWcKCU1hdGNoIGpvdXJuYWxkCglSdWxlICRMT0dLSU5EIGlmeGF1ZGl0IGlmeGF1ZGl0IGZhbHNlCgpbRklMVEVSXQoJTmFtZSByZXdyaXRlX3RhZwoJTWF0Y2ggam91cm5hbGQKCVJ1bGUgJExPR0tJTkQgb3V0Ym91bmRSZXF1ZXN0cyBvdXRib3VuZFJlcXVlc3RzIGZhbHNlCgpbRklMVEVSXQoJTmFtZSBtb2RpZnkKCU1hdGNoICBvdXRib3VuZFJlcXVlc3RzCglSZW1vdmUgQ0xJRU5UX1BSSU5DSVBBTF9OQU1FCglSZW1vdmUgRklMRQoJUmVtb3ZlIENPTVBPTkVOVAoKW09VVFBVVF0KCU5hbWUgZm9yd2FyZAoJTWF0Y2ggKgoJUG9ydCAyOTIzMApFT0YKCmVjaG8gIkZMVUVOVEJJVElNQUdFPSRGTFVFTlRCSVRJTUFHRSIgPi9ldGMvc3lzY29uZmlnL2ZsdWVudGJpdAoKY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL2ZsdWVudGJpdC5zZXJ2aWNlIDw8J0VPRicKW1VuaXRdCkFmdGVyPW5ldHdvcmstb25saW5lLnRhcmdldApXYW50cz1uZXR3b3JrLW9ubGluZS50YXJnZXQKU3RhcnRMaW1pdEludGVydmFsU2VjPTAKCltTZXJ2aWNlXQpSZXN0YXJ0U2VjPTFzCkVudmlyb25tZW50RmlsZT0vZXRjL3N5c2NvbmZpZy9mbHVlbnRiaXQKRXhlY1N0YXJ0UHJlPS0vdXNyL2Jpbi9kb2NrZXIgcm0gLWYgJU4KRXhlY1N0YXJ0PS91c3IvYmluL2RvY2tlciBydW4gXAogIC0tc2VjdXJpdHktb3B0IGxhYmVsPWRpc2FibGUgXAogIC0tZW50cnlwb2ludCAvb3B0L3RkLWFnZW50LWJpdC9iaW4vdGQtYWdlbnQtYml0IFwKICAtLW5ldD1ob3N0IFwKICAtLWhvc3RuYW1lICVIIFwKICAtLW5hbWUgJU4gXAogIC0tcm0gXAogIC0tY2FwLWRyb3AgbmV0X3JhdyBcCiAgLXYgL2V0Yy9mbHVlbnRiaXQvZmx1ZW50Yml0LmNvbmY6L2V0Yy9mbHVlbnRiaXQvZmx1ZW50Yml0LmNvbmYgXAogIC12IC92YXIvbGliL2ZsdWVudDovdmFyL2xpYi9mbHVlbnQ6eiBcCiAgLXYgL3Zhci9sb2cvam91cm5hbDovdmFyL2xvZy9qb3VybmFsOnJvIFwKICAtdiAvZXRjL21hY2hpbmUtaWQ6L2V0Yy9tYWNoaW5lLWlkOnJvIFwKICAkRkxVRU5UQklUSU1BR0UgXAogIC1jIC9ldGMvZmx1ZW50Yml0L2ZsdWVudGJpdC5jb25mCgpFeGVjU3RvcD0vdXNyL2Jpbi9kb2NrZXIgc3RvcCAlTgpSZXN0YXJ0PWFsd2F5cwpSZXN0YXJ0U2VjPTUKU3RhcnRMaW1pdEludGVydmFsPTAKCltJbnN0YWxsXQpXYW50ZWRCeT1tdWx0aS11c2VyLnRhcmdldApFT0YKCm1rZGlyIC9ldGMvYXJvLXJwCmJhc2U2NCAtZCA8PDwiJEFETUlOQVBJQ0FCVU5ETEUiID4vZXRjL2Fyby1ycC9hZG1pbi1jYS1idW5kbGUucGVtCmlmIFtbIC1uICIkQVJNQVBJQ0FCVU5ETEUiIF1dOyB0aGVuCiAgYmFzZTY0IC1kIDw8PCIkQVJNQVBJQ0FCVU5ETEUiID4vZXRjL2Fyby1ycC9hcm0tY2EtYnVuZGxlLnBlbQpmaQpjaG93biAtUiAxMDAwOjEwMDAgL2V0Yy9hcm8tcnAKCmVjaG8gImNvbmZpZ3VyaW5nIG1kbSBzZXJ2aWNlIgpjYXQgPi9ldGMvc3lzY29uZmlnL21kbSA8PEVPRgpNRE1GUk9OVEVORFVSTD0nJE1ETUZST05URU5EVVJMJwpNRE1JTUFHRT0nJE1ETUlNQUdFJwpNRE1TT1VSQ0VFTlZJUk9OTUVOVD0nJExPQ0FUSU9OJwpNRE1TT1VSQ0VST0xFPXJwCk1ETVNPVVJDRVJPTEVJTlNUQU5DRT0nJChob3N0bmFtZSknCkVPRgoKbWtkaXIgL3Zhci9ldHcKY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL21kbS5zZXJ2aWNlIDw8J0VPRicKW1VuaXRdCkFmdGVyPW5ldHdvcmstb25saW5lLnRhcmdldApXYW50cz1uZXR3b3JrLW9ubGluZS50YXJnZXQKCltTZXJ2aWNlXQpFbnZpcm9ubWVudEZpbGU9L2V0Yy9zeXNjb25maWcvbWRtCkV4ZWNTdGFydFByZT0tL3Vzci9iaW4vZG9ja2VyIHJtIC1mICVOCkV4ZWNTdGFydD0vdXNyL2Jpbi9kb2NrZXIgcnVuIFwKICAtLWVudHJ5cG9pbnQgL3Vzci9zYmluL01ldHJpY3NFeHRlbnNpb24gXAogIC0taG9zdG5hbWUgJUggXAogIC0tbmFtZSAlTiBcCiAgLS1ybSBcCiAgLS1jYXAtZHJvcCBuZXRfcmF3IFwKICAtbSAyZyBcCiAgLXYgL2V0Yy9tZG0ucGVtOi9ldGMvbWRtLnBlbSBcCiAgLXYgL3Zhci9ldHc6L3Zhci9ldHc6eiBcCiAgJE1ETUlNQUdFIFwKICAtQ2VydEZpbGUgL2V0Yy9tZG0ucGVtIFwKICAtRnJvbnRFbmRVcmwgJE1ETUZST05URU5EVVJMIFwKICAtTG9nZ2VyIENvbnNvbGUgXAogIC1Mb2dMZXZlbCBXYXJuaW5nIFwKICAtUHJpdmF0ZUtleUZpbGUgL2V0Yy9tZG0ucGVtIFwKICAtU291cmNlRW52aXJvbm1lbnQgJE1ETVNPVVJDRUVOVklST05NRU5UIFwKICAtU291cmNlUm9sZSAkTURNU09VUkNFUk9MRSBcCiAgLVNvdXJjZVJvbGVJbnN0YW5jZSAkTURNU09VUkNFUk9MRUlOU1RBTkNFCkV4ZWNTdG9wPS91c3IvYmluL2RvY2tlciBzdG9wICVOClJlc3RhcnQ9YWx3YXlzClJlc3RhcnRTZWM9MQpTdGFydExpbWl0SW50ZXJ2YWw9MAoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIudGFyZ2V0CkVPRgoKZWNobyAiY29uZmlndXJpbmcgYXJvLXJwIHNlcnZpY2UiCmNhdCA+L2V0Yy9zeXNjb25maWcvYXJvLXJwIDw8RU9GCkFDUl9SRVNPVVJDRV9JRD0nJEFDUlJFU09VUkNFSUQnCkFETUlOX0FQSV9DTElFTlRfQ0VSVF9DT01NT05fTkFNRT0nJEFETUlOQVBJQ0xJRU5UQ0VSVENPTU1PTk5BTUUnCkFSTV9BUElfQ0xJRU5UX0NFUlRfQ09NTU9OX05BTUU9JyRBUk1BUElDTElFTlRDRVJUQ09NTU9OTkFNRScKQVpVUkVfQVJNX0NMSUVOVF9JRD0nJEFSTUNMSUVOVElEJwpBWlVSRV9GUF9DTElFTlRfSUQ9JyRGUENMSUVOVElEJwpBWlVSRV9GUF9TRVJWSUNFX1BSSU5DSVBBTF9JRD0nJEZQU0VSVklDRVBSSU5DSVBBTElEJwpDTFVTVEVSX01ETV9BQ0NPVU5UPSckQ0xVU1RFUk1ETUFDQ09VTlQnCkNMVVNURVJfTURNX05BTUVTUEFDRT1SUApDTFVTVEVSX01EU0RfQUNDT1VOVD0nJENMVVNURVJNRFNEQUNDT1VOVCcKQ0xVU1RFUl9NRFNEX0NPTkZJR19WRVJTSU9OPSckQ0xVU1RFUk1EU0RDT05GSUdWRVJTSU9OJwpDTFVTVEVSX01EU0RfTkFNRVNQQUNFPSckQ0xVU1RFUk1EU0ROQU1FU1BBQ0UnCkRBVEFCQVNFX0FDQ09VTlRfTkFNRT0nJERBVEFCQVNFQUNDT1VOVE5BTUUnCkRPTUFJTl9OQU1FPSckTE9DQVRJT04uJENMVVNURVJQQVJFTlRET01BSU5OQU1FJwpHQVRFV0FZX0RPTUFJTlM9JyRHQVRFV0FZRE9NQUlOUycKR0FURVdBWV9SRVNPVVJDRUdST1VQPSckR0FURVdBWVJFU09VUkNFR1JPVVBOQU1FJwpLRVlWQVVMVF9QUkVGSVg9JyRLRVlWQVVMVFBSRUZJWCcKTURNX0FDQ09VTlQ9JyRSUE1ETUFDQ09VTlQnCk1ETV9OQU1FU1BBQ0U9UlAKTURTRF9FTlZJUk9OTUVOVD0nJE1EU0RFTlZJUk9OTUVOVCcKUlBfRkVBVFVSRVM9JyRSUEZFQVRVUkVTJwpSUElNQUdFPSckUlBJTUFHRScKQVJPX0lOU1RBTExfVklBX0hJVkU9JyRDTFVTVEVSU0lOU1RBTExWSUFISVZFJwpBUk9fSElWRV9ERUZBVUxUX0lOU1RBTExFUl9QVUxMU1BFQz0nJENMVVNURVJERUZBVUxUSU5TVEFMTEVSUFVMTFNQRUMnCkFST19BRE9QVF9CWV9ISVZFPSckQ0xVU1RFUlNBRE9QVEJZSElWRScKVVNFX0NIRUNLQUNDRVNTPSckVVNFQ0hFQ0tBQ0NFU1MnCk9JRENfQUZEX0VORFBPSU5UPSckTE9DQVRJT04ub2ljLiRSUFBBUkVOVERPTUFJTk5BTUUnCk9JRENfU1RPUkFHRV9BQ0NPVU5UX05BTUU9JyRPSURDU1RPUkFHRUFDQ09VTlROQU1FJwpFT0YKCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9hcm8tcnAuc2VydmljZSA8PCdFT0YnCltVbml0XQpBZnRlcj1uZXR3b3JrLW9ubGluZS50YXJnZXQKV2FudHM9bmV0d29yay1vbmxpbmUudGFyZ2V0CgpbU2VydmljZV0KRW52aXJvbm1lbnRGaWxlPS9ldGMvc3lzY29uZmlnL2Fyby1ycApFeGVjU3RhcnRQcmU9LS91c3IvYmluL2RvY2tlciBybSAtZiAlTgpFeGVjU3RhcnQ9L3Vzci9iaW4vZG9ja2VyIHJ1biBcCiAgLS1ob3N0bmFtZSAlSCBcCiAgLS1uYW1lICVOIFwKICAtLXJtIFwKICAtLWNhcC1kcm9wIG5ldF9yYXcgXAogIC1lIEFDUl9SRVNPVVJDRV9JRCBcCiAgLWUgQURNSU5fQVBJX0NMSUVOVF9DRVJUX0NPTU1PTl9OQU1FIFwKICAtZSBBUk1fQVBJX0NMSUVOVF9DRVJUX0NPTU1PTl9OQU1FIFwKICAtZSBBWlVSRV9BUk1fQ0xJRU5UX0lEIFwKICAtZSBBWlVSRV9GUF9DTElFTlRfSUQgXAogIC1lIENMVVNURVJfTURNX0FDQ09VTlQgXAogIC1lIENMVVNURVJfTURNX05BTUVTUEFDRSBcCiAgLWUgQ0xVU1RFUl9NRFNEX0FDQ09VTlQgXAogIC1lIENMVVNURVJfTURTRF9DT05GSUdfVkVSU0lPTiBcCiAgLWUgQ0xVU1RFUl9NRFNEX05BTUVTUEFDRSBcCiAgLWUgREFUQUJBU0VfQUNDT1VOVF9OQU1FIFwKICAtZSBET01BSU5fTkFNRSBcCiAgLWUgR0FURVdBWV9ET01BSU5TIFwKICAtZSBHQVRFV0FZX1JFU09VUkNFR1JPVVAgXAogIC1lIEtFWVZBVUxUX1BSRUZJWCBcCiAgLWUgTURNX0FDQ09VTlQgXAogIC1lIE1ETV9OQU1FU1BBQ0UgXAogIC1lIE1EU0RfRU5WSVJPTk1FTlQgXAogIC1lIFJQX0ZFQVRVUkVTIFwKICAtZSBBUk9fSU5TVEFMTF9WSUFfSElWRSBcCiAgLWUgQVJPX0hJVkVfREVGQVVMVF9JTlNUQUxMRVJfUFVMTFNQRUMgXAogIC1lIEFST19BRE9QVF9CWV9ISVZFIFwKICAtZSBVU0VfQ0hFQ0tBQ0NFU1MgXAogIC1lIE9JRENfQUZEX0VORFBPSU5UIFwKICAtZSBPSURDX1NUT1JBR0VfQUNDT1VOVF9OQU1FIFwKICAtbSAyZyBcCiAgLXAgNDQzOjg0NDMgXAogIC12IC9ldGMvYXJvLXJwOi9ldGMvYXJvLXJwIFwKICAtdiAvcnVuL3N5c3RlbWQvam91cm5hbDovcnVuL3N5c3RlbWQvam91cm5hbCBcCiAgLXYgL3Zhci9ldHc6L3Zhci9ldHc6eiBcCiAgJFJQSU1BR0UgXAogIHJwCkV4ZWNTdG9wPS91c3IvYmluL2RvY2tlciBzdG9wIC10IDM2MDAgJU4KVGltZW91dFN0b3BTZWM9MzYwMApSZXN0YXJ0PWFsd2F5cwpSZXN0YXJ0U2VjPTEKU3RhcnRMaW1pdEludGVydmFsPTAKCltJbnN0YWxsXQpXYW50ZWRCeT1tdWx0aS11c2VyLnRhcmdldApFT0YKCiMgRE9NQUlOX05BTUUsIENMVVNURVJfTURTRF9BQ0NPVU5ULCBDTFVTVEVSX01EU0RfQ09ORklHX1ZFUlNJT04sIEdBVEVXQVlfRE9NQUlOUywgR0FURVdBWV9SRVNPVVJDRUdST1VQLCBNRFNEX0VOVklST05NRU5UIENMVVNURVJfTURTRF9OQU1FU1BBQ0UKIyBhcmUgbm90IHVzZWQsIGJ1dCBjYW4ndCBlYXNpbHkgYmUgcmVmYWN0b3JlZCBvdXQuIFNob3VsZCBiZSByZXZpc2l0ZWQgaW4gdGhlIGZ1dHVyZS4KZWNobyAiY29uZmlndXJpbmcgYXJvLW1vbml0b3Igc2VydmljZSIKY2F0ID4vZXRjL3N5c2NvbmZpZy9hcm8tbW9uaXRvciA8PEVPRgpBWlVSRV9GUF9DTElFTlRfSUQ9JyRGUENMSUVOVElEJwpET01BSU5fTkFNRT0nJExPQ0FUSU9OLiRDTFVTVEVSUEFSRU5URE9NQUlOTkFNRScKQ0xVU1RFUl9NRFNEX0FDQ09VTlQ9JyRDTFVTVEVSTURTREFDQ09VTlQnCkNMVVNURVJfTURTRF9DT05GSUdfVkVSU0lPTj0nJENMVVNURVJNRFNEQ09ORklHVkVSU0lPTicKR0FURVdBWV9ET01BSU5TPSckR0FURVdBWURPTUFJTlMnCkdBVEVXQVlfUkVTT1VSQ0VHUk9VUD0nJEdBVEVXQVlSRVNPVVJDRUdST1VQTkFNRScKTURTRF9FTlZJUk9OTUVOVD0nJE1EU0RFTlZJUk9OTUVOVCcKQ0xVU1RFUl9NRFNEX05BTUVTUEFDRT0nJENMVVNURVJNRFNETkFNRVNQQUNFJwpDTFVTVEVSX01ETV9BQ0NPVU5UPSckQ0xVU1RFUk1ETUFDQ09VTlQnCkNMVVNURVJfTURNX05BTUVTUEFDRT1CQk0KREFUQUJBU0VfQUNDT1VOVF9OQU1FPSckREFUQUJBU0VBQ0NPVU5UTkFNRScKS0VZVkFVTFRfUFJFRklYPSckS0VZVkFVTFRQUkVGSVgnCk1ETV9BQ0NPVU5UPSckUlBNRE1BQ0NPVU5UJwpNRE1fTkFNRVNQQUNFPUJCTQpSUElNQUdFPSckUlBJTUFHRScKRU9GCgpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vYXJvLW1vbml0b3Iuc2VydmljZSA8PCdFT0YnCltVbml0XQpBZnRlcj1uZXR3b3JrLW9ubGluZS50YXJnZXQKV2FudHM9bmV0d29yay1vbmxpbmUudGFyZ2V0CgpbU2VydmljZV0KRW52aXJvbm1lbnRGaWxlPS9ldGMvc3lzY29uZmlnL2Fyby1tb25pdG9yCkV4ZWNTdGFydFByZT0tL3Vzci9iaW4vZG9ja2VyIHJtIC1mICVOCkV4ZWNTdGFydD0vdXNyL2Jpbi9kb2NrZXIgcnVuIFwKICAtLWhvc3RuYW1lICVIIFwKICAtLW5hbWUgJU4gXAogIC0tcm0gXAogIC0tY2FwLWRyb3AgbmV0X3JhdyBcCiAgLWUgQVpVUkVfRlBfQ0xJRU5UX0lEIFwKICAtZSBET01BSU5fTkFNRSBcCiAgLWUgQ0xVU1RFUl9NRFNEX0FDQ09VTlQgXAogIC1lIENMVVNURVJfTURTRF9DT05GSUdfVkVSU0lPTiBcCiAgLWUgR0FURVdBWV9ET01BSU5TIFwKICAtZSBHQVRFV0FZX1JFU09VUkNFR1JPVVAgXAogIC1lIE1EU0RfRU5WSVJPTk1FTlQgXAogIC1lIENMVVNURVJfTURTRF9OQU1FU1BBQ0UgXAogIC1lIENMVVNURVJfTURNX0FDQ09VTlQgXAogIC1lIENMVVNURVJfTURNX05BTUVTUEFDRSBcCiAgLWUgREFUQUJBU0VfQUNDT1VOVF9OQU1FIFwKICAtZSBLRVlWQVVMVF9QUkVGSVggXAogIC1lIE1ETV9BQ0NPVU5UIFwKICAtZSBNRE1fTkFNRVNQQUNFIFwKICAtbSAyLjVnIFwKICAtdiAvcnVuL3N5c3RlbWQvam91cm5hbDovcnVuL3N5c3RlbWQvam91cm5hbCBcCiAgLXYgL3Zhci9ldHc6L3Zhci9ldHc6eiBcCiAgJFJQSU1BR0UgXAogIG1vbml0b3IKUmVzdGFydD1hbHdheXMKUmVzdGFydFNlYz0xClN0YXJ0TGltaXRJbnRlcnZhbD0wCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQKRU9GCgplY2hvICJjb25maWd1cmluZyBhcm8tcG9ydGFsIHNlcnZpY2UiCmNhdCA+L2V0Yy9zeXNjb25maWcvYXJvLXBvcnRhbCA8PEVPRgpBWlVSRV9QT1JUQUxfQUNDRVNTX0dST1VQX0lEUz0nJFBPUlRBTEFDQ0VTU0dST1VQSURTJwpBWlVSRV9QT1JUQUxfQ0xJRU5UX0lEPSckUE9SVEFMQ0xJRU5USUQnCkFaVVJFX1BPUlRBTF9FTEVWQVRFRF9HUk9VUF9JRFM9JyRQT1JUQUxFTEVWQVRFREdST1VQSURTJwpEQVRBQkFTRV9BQ0NPVU5UX05BTUU9JyREQVRBQkFTRUFDQ09VTlROQU1FJwpLRVlWQVVMVF9QUkVGSVg9JyRLRVlWQVVMVFBSRUZJWCcKTURNX0FDQ09VTlQ9JyRSUE1ETUFDQ09VTlQnCk1ETV9OQU1FU1BBQ0U9UG9ydGFsClBPUlRBTF9IT1NUTkFNRT0nJExPQ0FUSU9OLmFkbWluLiRSUFBBUkVOVERPTUFJTk5BTUUnClJQSU1BR0U9JyRSUElNQUdFJwpFT0YKCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9hcm8tcG9ydGFsLnNlcnZpY2UgPDwnRU9GJwpbVW5pdF0KQWZ0ZXI9bmV0d29yay1vbmxpbmUudGFyZ2V0CldhbnRzPW5ldHdvcmstb25saW5lLnRhcmdldApTdGFydExpbWl0SW50ZXJ2YWw9MAoKW1NlcnZpY2VdCkVudmlyb25tZW50RmlsZT0vZXRjL3N5c2NvbmZpZy9hcm8tcG9ydGFsCkV4ZWNTdGFydFByZT0tL3Vzci9iaW4vZG9ja2VyIHJtIC1mICVOCkV4ZWNTdGFydD0vdXNyL2Jpbi9kb2NrZXIgcnVuIFwKICAtLWhvc3RuYW1lICVIIFwKICAtLW5hbWUgJU4gXAogIC0tcm0gXAogIC0tY2FwLWRyb3AgbmV0X3JhdyBcCiAgLWUgQVpVUkVfUE9SVEFMX0FDQ0VTU19HUk9VUF9JRFMgXAogIC1lIEFaVVJFX1BPUlRBTF9DTElFTlRfSUQgXAogIC1lIEFaVVJFX1BPUlRBTF9FTEVWQVRFRF9HUk9VUF9JRFMgXAogIC1lIERBVEFCQVNFX0FDQ09VTlRfTkFNRSBcCiAgLWUgS0VZVkFVTFRfUFJFRklYIFwKICAtZSBNRE1fQUNDT1VOVCBcCiAgLWUgTURNX05BTUVTUEFDRSBcCiAgLWUgUE9SVEFMX0hPU1ROQU1FIFwKICAtbSAyZyBcCiAgLXAgNDQ0Ojg0NDQgXAogIC1wIDIyMjI6MjIyMiBcCiAgLXYgL3J1bi9zeXN0ZW1kL2pvdXJuYWw6L3J1bi9zeXN0ZW1kL2pvdXJuYWwgXAogIC12IC92YXIvZXR3Oi92YXIvZXR3OnogXAogICRSUElNQUdFIFwKICBwb3J0YWwKUmVzdGFydD1hbHdheXMKUmVzdGFydFNlYz0xCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQKRU9GCgplY2hvICJjb25maWd1cmluZyBtZHNkIGFuZCBtZG0gc2VydmljZXMiCmNoY29uIC1SIHN5c3RlbV91Om9iamVjdF9yOnZhcl9sb2dfdDpzMCAvdmFyL29wdC9taWNyb3NvZnQvbGludXhtb25hZ2VudAoKbWtkaXIgLXAgL3Zhci9saWIvd2FhZ2VudC9NaWNyb3NvZnQuQXp1cmUuS2V5VmF1bHQuU3RvcmUKCmZvciB2YXIgaW4gIm1kc2QiICJtZG0iOyBkbwpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vZG93bmxvYWQtJHZhci1jcmVkZW50aWFscy5zZXJ2aWNlIDw8RU9GCltVbml0XQpEZXNjcmlwdGlvbj1QZXJpb2RpYyAkdmFyIGNyZWRlbnRpYWxzIHJlZnJlc2gKCltTZXJ2aWNlXQpUeXBlPW9uZXNob3QKRXhlY1N0YXJ0PS91c3IvbG9jYWwvYmluL2Rvd25sb2FkLWNyZWRlbnRpYWxzLnNoICR2YXIKRU9GCgpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vZG93bmxvYWQtJHZhci1jcmVkZW50aWFscy50aW1lciA8PEVPRgpbVW5pdF0KRGVzY3JpcHRpb249UGVyaW9kaWMgJHZhciBjcmVkZW50aWFscyByZWZyZXNoCkFmdGVyPW5ldHdvcmstb25saW5lLnRhcmdldApXYW50cz1uZXR3b3JrLW9ubGluZS50YXJnZXQKCltUaW1lcl0KT25Cb290U2VjPTBtaW4KT25DYWxlbmRhcj0wLzEyOjAwOjAwCkFjY3VyYWN5U2VjPTVzCgpbSW5zdGFsbF0KV2FudGVkQnk9dGltZXJzLnRhcmdldApFT0YKZG9uZQoKY2F0ID4vdXNyL2xvY2FsL2Jpbi9kb3dubG9hZC1jcmVkZW50aWFscy5zaCA8PEVPRgojIS9iaW4vYmFzaApzZXQgLWV1CgpDT01QT05FTlQ9IlwkMSIKZWNobyAiRG93bmxvYWQgXCRDT01QT05FTlQgY3JlZGVudGlhbHMiCgpURU1QX0RJUj1cJChta3RlbXAgLWQpCmV4cG9ydCBBWlVSRV9DT05GSUdfRElSPVwkKG1rdGVtcCAtZCkKCmVjaG8gIkxvZ2dpbmcgaW50byBBenVyZS4uLiIKUkVUUklFUz0zCndoaWxlIFsgIlwkUkVUUklFUyIgLWd0IDAgXTsgZG8KICAgIGlmIGF6IGxvZ2luIC1pIC0tYWxsb3ctbm8tc3Vic2NyaXB0aW9ucwogICAgdGhlbgogICAgICAgIGVjaG8gImF6IGxvZ2luIHN1Y2Nlc3NmdWwiCiAgICAgICAgYnJlYWsKICAgIGVsc2UKICAgICAgICBlY2hvICJheiBsb2dpbiBmYWlsZWQuIFJldHJ5aW5nLi4uIgogICAgICAgIGxldCBSRVRSSUVTLT0xCiAgICAgICAgc2xlZXAgNQogICAgZmkKZG9uZQoKdHJhcCAiY2xlYW51cCIgRVhJVAoKY2xlYW51cCgpIHsKICBheiBsb2dvdXQKICBbWyAiXCRURU1QX0RJUiIgPX4gL3RtcC8uKyBdXSAmJiBybSAtcmYgXCRURU1QX0RJUgogIFtbICJcJEFaVVJFX0NPTkZJR19ESVIiID1+IC90bXAvLisgXV0gJiYgcm0gLXJmIFwkQVpVUkVfQ09ORklHX0RJUgp9CgppZiBbICJcJENPTVBPTkVOVCIgPSAibWRtIiBdOyB0aGVuCiAgQ1VSUkVOVF9DRVJUX0ZJTEU9Ii9ldGMvbWRtLnBlbSIKZWxpZiBbICJcJENPTVBPTkVOVCIgPSAibWRzZCIgXTsgdGhlbgogIENVUlJFTlRfQ0VSVF9GSUxFPSIvdmFyL2xpYi93YWFnZW50L01pY3Jvc29mdC5BenVyZS5LZXlWYXVsdC5TdG9yZS9tZHNkLnBlbSIKZWxzZQogIGVjaG8gSW52YWxpZCB1c2FnZSAmJiBleGl0IDEKZmkKClNFQ1JFVF9OQU1FPSJycC1cJHtDT01QT05FTlR9IgpORVdfQ0VSVF9GSUxFPSJcJFRFTVBfRElSL1wkQ09NUE9ORU5ULnBlbSIKZm9yIGF0dGVtcHQgaW4gezEuLjV9OyBkbwogIGF6IGtleXZhdWx0IHNlY3JldCBkb3dubG9hZCAtLWZpbGUgXCRORVdfQ0VSVF9GSUxFIC0taWQgImh0dHBzOi8vJEtFWVZBVUxUUFJFRklYLXN2Yy4kS0VZVkFVTFRETlNTVUZGSVgvc2VjcmV0cy9cJFNFQ1JFVF9OQU1FIiAmJiBicmVhawogIGlmIFtbIFwkYXR0ZW1wdCAtbHQgNSBdXTsgdGhlbiBzbGVlcCAxMDsgZWxzZSBleGl0IDE7IGZpCmRvbmUKCmlmIFsgLWYgXCRORVdfQ0VSVF9GSUxFIF07IHRoZW4KICBpZiBbICJcJENPTVBPTkVOVCIgPSAibWRzZCIgXTsgdGhlbgogICAgY2hvd24gc3lzbG9nOnN5c2xvZyBcJE5FV19DRVJUX0ZJTEUKICBlbHNlCiAgICBzZWQgLWkgLW5lICcxLC9FTkQgQ0VSVElGSUNBVEUvIHAnIFwkTkVXX0NFUlRfRklMRQogIGZpCgogIG5ld19jZXJ0X3NuPSJcJChvcGVuc3NsIHg1MDkgLWluICJcJE5FV19DRVJUX0ZJTEUiIC1ub291dCAtc2VyaWFsIHwgYXdrIC1GPSAne3ByaW50IFwkMn0nKSIKICBjdXJyZW50X2NlcnRfc249IlwkKG9wZW5zc2wgeDUwOSAtaW4gIlwkQ1VSUkVOVF9DRVJUX0ZJTEUiIC1ub291dCAtc2VyaWFsIHwgYXdrIC1GPSAne3ByaW50IFwkMn0nKSIKICBpZiBbWyAhIC16IFwkbmV3X2NlcnRfc24gXV0gJiYgW1sgXCRuZXdfY2VydF9zbiAhPSAiXCRjdXJyZW50X2NlcnRfc24iIF1dOyB0aGVuCiAgICBlY2hvIHVwZGF0aW5nIGNlcnRpZmljYXRlIGZvciBcJENPTVBPTkVOVAogICAgY2htb2QgMDYwMCBcJE5FV19DRVJUX0ZJTEUKICAgIG12IFwkTkVXX0NFUlRfRklMRSBcJENVUlJFTlRfQ0VSVF9GSUxFCiAgZmkKZWxzZQogIGVjaG8gRmFpbGVkIHRvIHJlZnJlc2ggY2VydGlmaWNhdGUgZm9yIFwkQ09NUE9ORU5UICYmIGV4aXQgMQpmaQpFT0YKCmNobW9kIHUreCAvdXNyL2xvY2FsL2Jpbi9kb3dubG9hZC1jcmVkZW50aWFscy5zaAoKc3lzdGVtY3RsIGVuYWJsZSBkb3dubG9hZC1tZHNkLWNyZWRlbnRpYWxzLnRpbWVyCnN5c3RlbWN0bCBlbmFibGUgZG93bmxvYWQtbWRtLWNyZWRlbnRpYWxzLnRpbWVyCgovdXNyL2xvY2FsL2Jpbi9kb3dubG9hZC1jcmVkZW50aWFscy5zaCBtZHNkCi91c3IvbG9jYWwvYmluL2Rvd25sb2FkLWNyZWRlbnRpYWxzLnNoIG1kbQpNRFNEQ0VSVElGSUNBVEVTQU49JChvcGVuc3NsIHg1MDkgLWluIC92YXIvbGliL3dhYWdlbnQvTWljcm9zb2Z0LkF6dXJlLktleVZhdWx0LlN0b3JlL21kc2QucGVtIC1ub291dCAtc3ViamVjdCB8IHNlZCAtZSAncy8uKkNOID0gLy8nKQoKY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL3dhdGNoLW1kbS1jcmVkZW50aWFscy5zZXJ2aWNlIDw8RU9GCltVbml0XQpEZXNjcmlwdGlvbj1XYXRjaCBmb3IgY2hhbmdlcyBpbiBtZG0ucGVtIGFuZCByZXN0YXJ0cyB0aGUgbWRtIHNlcnZpY2UKCltTZXJ2aWNlXQpUeXBlPW9uZXNob3QKRXhlY1N0YXJ0PS91c3IvYmluL3N5c3RlbWN0bCByZXN0YXJ0IG1kbS5zZXJ2aWNlCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQKRU9GCgpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vd2F0Y2gtbWRtLWNyZWRlbnRpYWxzLnBhdGggPDxFT0YKW1BhdGhdClBhdGhNb2RpZmllZD0vZXRjL21kbS5wZW0KCltJbnN0YWxsXQpXYW50ZWRCeT1tdWx0aS11c2VyLnRhcmdldApFT0YKCnN5c3RlbWN0bCBlbmFibGUgd2F0Y2gtbWRtLWNyZWRlbnRpYWxzLnBhdGgKc3lzdGVtY3RsIHN0YXJ0IHdhdGNoLW1kbS1jcmVkZW50aWFscy5wYXRoCgpta2RpciAvZXRjL3N5c3RlbWQvc3lzdGVtL21kc2Quc2VydmljZS5kCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9tZHNkLnNlcnZpY2UuZC9vdmVycmlkZS5jb25mIDw8J0VPRicKW1VuaXRdCkFmdGVyPW5ldHdvcmstb25saW5lLnRhcmdldApFT0YKCmNhdCA+L2V0Yy9kZWZhdWx0L21kc2QgPDxFT0YKTURTRF9ST0xFX1BSRUZJWD0vdmFyL3J1bi9tZHNkL2RlZmF1bHQKTURTRF9PUFRJT05TPSItQSAtZCAtciBcJE1EU0RfUk9MRV9QUkVGSVgiCgpleHBvcnQgTU9OSVRPUklOR19HQ1NfRU5WSVJPTk1FTlQ9JyRNRFNERU5WSVJPTk1FTlQnCmV4cG9ydCBNT05JVE9SSU5HX0dDU19BQ0NPVU5UPSckUlBNRFNEQUNDT1VOVCcKZXhwb3J0IE1PTklUT1JJTkdfR0NTX1JFR0lPTj0nJExPQ0FUSU9OJwpleHBvcnQgTU9OSVRPUklOR19HQ1NfQVVUSF9JRF9UWVBFPUF1dGhLZXlWYXVsdApleHBvcnQgTU9OSVRPUklOR19HQ1NfQVVUSF9JRD0nJE1EU0RDRVJUSUZJQ0FURVNBTicKZXhwb3J0IE1PTklUT1JJTkdfR0NTX05BTUVTUEFDRT0nJFJQTURTRE5BTUVTUEFDRScKZXhwb3J0IE1PTklUT1JJTkdfQ09ORklHX1ZFUlNJT049JyRSUE1EU0RDT05GSUdWRVJTSU9OJwpleHBvcnQgTU9OSVRPUklOR19VU0VfR0VORVZBX0NPTkZJR19TRVJWSUNFPXRydWUKCmV4cG9ydCBNT05JVE9SSU5HX1RFTkFOVD0nJExPQ0FUSU9OJwpleHBvcnQgTU9OSVRPUklOR19ST0xFPXJwCmV4cG9ydCBNT05JVE9SSU5HX1JPTEVfSU5TVEFOQ0U9JyQoaG9zdG5hbWUpJwoKZXhwb3J0IE1EU0RfTVNHUEFDS19TT1JUX0NPTFVNTlM9MQpFT0YKCiMgc2V0dGluZyBNT05JVE9SSU5HX0dDU19BVVRIX0lEX1RZUEU9QXV0aEtleVZhdWx0IHNlZW1zIHRvIGhhdmUgY2F1c2VkIG1kc2Qgbm90CiMgdG8gaG9ub3VyIFNTTF9DRVJUX0ZJTEUgYW55IG1vcmUsIGhlYXZlbiBvbmx5IGtub3dzIHdoeS4KbWtkaXIgLXAgL3Vzci9saWIvc3NsL2NlcnRzCmNzcGxpdCAtZiAvdXNyL2xpYi9zc2wvY2VydHMvY2VydC0gLWIgJTAzZC5wZW0gL2V0Yy9wa2kvdGxzL2NlcnRzL2NhLWJ1bmRsZS5jcnQgL14kLzEgeyp9ID4vZGV2L251bGwKY19yZWhhc2ggL3Vzci9saWIvc3NsL2NlcnRzCgojIHdlIGxlYXZlIGNsaWVudElkIGJsYW5rIGFzIGxvbmcgYXMgb25seSAxIG1hbmFnZWQgaWRlbnRpdHkgYXNzaWduZWQgdG8gdm1zcwojIGlmIHdlIGhhdmUgbW9yZSB0aGFuIDEsIHdlIHdpbGwgbmVlZCB0byBwb3B1bGF0ZSB3aXRoIGNsaWVudElkIHVzZWQgZm9yIG9mZi1ub2RlIHNjYW5uaW5nCmNhdCA+L2V0Yy9kZWZhdWx0L3ZzYS1ub2Rlc2Nhbi1hZ2VudC5jb25maWcgPDxFT0YKewogICAgIk5pY2UiOiAxOSwKICAgICJUaW1lb3V0IjogMTA4MDAsCiAgICAiQ2xpZW50SWQiOiAiIiwKICAgICJUZW5hbnRJZCI6ICIkQVpVUkVTRUNQQUNLVlNBVEVOQU5USUQiLAogICAgIlF1YWx5c1N0b3JlQmFzZVVybCI6ICIkQVpVUkVTRUNQQUNLUVVBTFlTVVJMIiwKICAgICJQcm9jZXNzVGltZW91dCI6IDMwMCwKICAgICJDb21tYW5kRGVsYXkiOiAwCiAgfQpFT0YKCmVjaG8gImVuYWJsaW5nIGFybyBzZXJ2aWNlcyIKZm9yIHNlcnZpY2UgaW4gYXJvLW1vbml0b3IgYXJvLXBvcnRhbCBhcm8tcnAgYXVvbXMgYXpzZWNkIGF6c2VjbW9uZCBtZHNkIG1kbSBjaHJvbnlkIGZsdWVudGJpdDsgZG8KICBzeXN0ZW1jdGwgZW5hYmxlICRzZXJ2aWNlLnNlcnZpY2UKZG9uZQoKZm9yIHNjYW4gaW4gYmFzZWxpbmUgY2xhbWF2IHNvZnR3YXJlOyBkbwogIC91c3IvbG9jYWwvYmluL2F6c2VjZCBjb25maWcgLXMgJHNjYW4gLWQgUDFECmRvbmUKCmVjaG8gInJlYm9vdGluZyIKcmVzdG9yZWNvbiAtUkYgL3Zhci9sb2cvKgooc2xlZXAgMzA7IHJlYm9vdCkgJgo=')))]"
+                                        "script": "[base64(concat(base64ToString('c2V0IC1leAoK'),'ACRRESOURCEID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('acrResourceId')),''')\n','ADMINAPICLIENTCERTCOMMONNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('adminApiClientCertCommonName')),''')\n','ARMAPICLIENTCERTCOMMONNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('armApiClientCertCommonName')),''')\n','ARMCLIENTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('armClientId')),''')\n','AZURECLOUDNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureCloudName')),''')\n','AZURESECPACKQUALYSURL=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureSecPackQualysUrl')),''')\n','AZURESECPACKVSATENANTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureSecPackVSATenantId')),''')\n','CLUSTERMDMACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterMdmAccount')),''')\n','CLUSTERMDSDACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterMdsdAccount')),''')\n','CLUSTERMDSDCONFIGVERSION=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterMdsdConfigVersion')),''')\n','CLUSTERMDSDNAMESPACE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterMdsdNamespace')),''')\n','CLUSTERPARENTDOMAINNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterParentDomainName')),''')\n','DATABASEACCOUNTNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('databaseAccountName')),''')\n','FLUENTBITIMAGE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('fluentbitImage')),''')\n','FPCLIENTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('fpClientId')),''')\n','FPSERVICEPRINCIPALID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('fpServicePrincipalId')),''')\n','GATEWAYDOMAINS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayDomains')),''')\n','GATEWAYRESOURCEGROUPNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayResourceGroupName')),''')\n','GATEWAYSERVICEPRINCIPALID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayServicePrincipalId')),''')\n','KEYVAULTDNSSUFFIX=$(base64 -d \u003c\u003c\u003c''',base64(parameters('keyvaultDNSSuffix')),''')\n','KEYVAULTPREFIX=$(base64 -d \u003c\u003c\u003c''',base64(parameters('keyvaultPrefix')),''')\n','MDMFRONTENDURL=$(base64 -d \u003c\u003c\u003c''',base64(parameters('mdmFrontendUrl')),''')\n','MDSDENVIRONMENT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('mdsdEnvironment')),''')\n','PORTALACCESSGROUPIDS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('portalAccessGroupIds')),''')\n','PORTALCLIENTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('portalClientId')),''')\n','PORTALELEVATEDGROUPIDS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('portalElevatedGroupIds')),''')\n','RPFEATURES=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpFeatures')),''')\n','RPIMAGE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpImage')),''')\n','RPMDMACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdmAccount')),''')\n','RPMDSDACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdsdAccount')),''')\n','RPMDSDCONFIGVERSION=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdsdConfigVersion')),''')\n','RPMDSDNAMESPACE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdsdNamespace')),''')\n','RPPARENTDOMAINNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpParentDomainName')),''')\n','OIDCSTORAGEACCOUNTNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('oidcStorageAccountName')),''')\n','CLUSTERSINSTALLVIAHIVE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clustersInstallViaHive')),''')\n','CLUSTERSADOPTBYHIVE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clustersAdoptByHive')),''')\n','CLUSTERDEFAULTINSTALLERPULLSPEC=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterDefaultInstallerPullspec')),''')\n','USECHECKACCESS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('useCheckAccess')),''')\n','ADMINAPICABUNDLE=''',parameters('adminApiCaBundle'),'''\n','ARMAPICABUNDLE=''',parameters('armApiCaBundle'),'''\n','MDMIMAGE=''/distroless/genevamdm:2.2024.517.533-b73893-20240522t0954@sha256:939df9d7b6660874697f8ebed1fe56504f86d92f99801a9dc6fd98e9176d3f75''\n','LOCATION=$(base64 -d \u003c\u003c\u003c''',base64(resourceGroup().location),''')\n','SUBSCRIPTIONID=$(base64 -d \u003c\u003c\u003c''',base64(subscription().subscriptionId),''')\n','RESOURCEGROUPNAME=$(base64 -d \u003c\u003c\u003c''',base64(resourceGroup().name),''')\n','\n',base64ToString('IyEvYmluL2Jhc2gKCmVjaG8gInNldHRpbmcgc3NoIHBhc3N3b3JkIGF1dGhlbnRpY2F0aW9uIgojIFdlIG5lZWQgdG8gbWFudWFsbHkgc2V0IFBhc3N3b3JkQXV0aGVudGljYXRpb24gdG8gdHJ1ZSBpbiBvcmRlciBmb3IgdGhlIFZNU1MgQWNjZXNzIEpJVCB0byB3b3JrCnNlZCAtaSAncy9QYXNzd29yZEF1dGhlbnRpY2F0aW9uIG5vL1Bhc3N3b3JkQXV0aGVudGljYXRpb24geWVzL2cnIC9ldGMvc3NoL3NzaGRfY29uZmlnCnN5c3RlbWN0bCByZWxvYWQgc3NoZC5zZXJ2aWNlCgojQWRkaW5nIHJldHJ5IGxvZ2ljIHRvIHl1bSBjb21tYW5kcyBpbiBvcmRlciB0byBhdm9pZCBzdGFsbGluZyBvdXQgb24gcmVzb3VyY2UgbG9ja3MKZWNobyAicnVubmluZyBSSFVJIGZpeCIKZm9yIGF0dGVtcHQgaW4gezEuLjYwfTsgZG8KICB5dW0gdXBkYXRlIC15IC0tZGlzYWJsZXJlcG89JyonIC0tZW5hYmxlcmVwbz0ncmh1aS1taWNyb3NvZnQtYXp1cmUqJyAmJiBicmVhawogIGlmIFtbICR7YXR0ZW1wdH0gLWx0IDYwIF1dOyB0aGVuIHNsZWVwIDMwOyBlbHNlIGV4aXQgMTsgZmkKZG9uZQoKZWNobyAicnVubmluZyB5dW0gdXBkYXRlIgpmb3IgYXR0ZW1wdCBpbiB7MS4uNjB9OyBkbwogIHl1bSAteSAteCBXQUxpbnV4QWdlbnQgLXggV0FMaW51eEFnZW50LXVkZXYgdXBkYXRlIC0tYWxsb3dlcmFzaW5nICYmIGJyZWFrCiAgaWYgW1sgJHthdHRlbXB0fSAtbHQgNjAgXV07IHRoZW4gc2xlZXAgMzA7IGVsc2UgZXhpdCAxOyBmaQpkb25lCgplY2hvICJleHRlbmRpbmcgcGFydGl0aW9uIHRhYmxlIgojIExpbnV4IGJsb2NrIGRldmljZXMgYXJlIGluY29uc2lzdGVudGx5IG5hbWVkCiMgaXQncyBkaWZmaWN1bHQgdG8gdGllIHRoZSBsdm0gcHYgdG8gdGhlIHBoeXNpY2FsIGRpc2sgdXNpbmcgL2Rldi9kaXNrIGZpbGVzLCB3aGljaCBpcyB3aHkgbHZzIGlzIHVzZWQgaGVyZQpwaHlzaWNhbERpc2s9IiQobHZzIC1vIGRldmljZXMgLWEgfCBoZWFkIC1uMiB8IHRhaWwgLW4xIHwgY3V0IC1kICcgJyAtZiAzIHwgY3V0IC1kIFwoIC1mIDEgfCB0ciAtZCAnWzpkaWdpdDpdJykiCmdyb3dwYXJ0ICIkcGh5c2ljYWxEaXNrIiAyCgplY2hvICJleHRlbmRpbmcgZmlsZXN5c3RlbXMiCmx2ZXh0ZW5kIC1sICsyMCVGUkVFIC9kZXYvcm9vdHZnL3Jvb3Rsdgp4ZnNfZ3Jvd2ZzIC8KCmx2ZXh0ZW5kIC1sICsxMDAlRlJFRSAvZGV2L3Jvb3R2Zy92YXJsdgp4ZnNfZ3Jvd2ZzIC92YXIKCmVjaG8gImltcG9ydGluZyBycG0gcmVwb3NpdG9yaWVzIgpycG0gLS1pbXBvcnQgaHR0cHM6Ly9kbC5mZWRvcmFwcm9qZWN0Lm9yZy9wdWIvZXBlbC9SUE0tR1BHLUtFWS1FUEVMLTgKcnBtIC0taW1wb3J0IGh0dHBzOi8vcGFja2FnZXMubWljcm9zb2Z0LmNvbS9rZXlzL21pY3Jvc29mdC5hc2MKCmZvciBhdHRlbXB0IGluIHsxLi42MH07IGRvCiAgeXVtIC15IGluc3RhbGwgaHR0cHM6Ly9kbC5mZWRvcmFwcm9qZWN0Lm9yZy9wdWIvZXBlbC9lcGVsLXJlbGVhc2UtbGF0ZXN0LTgubm9hcmNoLnJwbSAmJiBicmVhawogIGlmIFtbICR7YXR0ZW1wdH0gLWx0IDYwIF1dOyB0aGVuIHNsZWVwIDMwOyBlbHNlIGV4aXQgMTsgZmkKZG9uZQoKZWNobyAiY29uZmlndXJpbmcgbG9ncm90YXRlIgpjYXQgPi9ldGMvbG9ncm90YXRlLmNvbmYgPDwnRU9GJwojIHNlZSAibWFuIGxvZ3JvdGF0ZSIgZm9yIGRldGFpbHMKIyByb3RhdGUgbG9nIGZpbGVzIHdlZWtseQp3ZWVrbHkKCiMga2VlcCAyIHdlZWtzIHdvcnRoIG9mIGJhY2tsb2dzCnJvdGF0ZSAyCgojIGNyZWF0ZSBuZXcgKGVtcHR5KSBsb2cgZmlsZXMgYWZ0ZXIgcm90YXRpbmcgb2xkIG9uZXMKY3JlYXRlCgojIHVzZSBkYXRlIGFzIGEgc3VmZml4IG9mIHRoZSByb3RhdGVkIGZpbGUKZGF0ZWV4dAoKIyB1bmNvbW1lbnQgdGhpcyBpZiB5b3Ugd2FudCB5b3VyIGxvZyBmaWxlcyBjb21wcmVzc2VkCmNvbXByZXNzCgojIFJQTSBwYWNrYWdlcyBkcm9wIGxvZyByb3RhdGlvbiBpbmZvcm1hdGlvbiBpbnRvIHRoaXMgZGlyZWN0b3J5CmluY2x1ZGUgL2V0Yy9sb2dyb3RhdGUuZAoKIyBubyBwYWNrYWdlcyBvd24gd3RtcCBhbmQgYnRtcCAtLSB3ZSdsbCByb3RhdGUgdGhlbSBoZXJlCi92YXIvbG9nL3d0bXAgewogICAgbW9udGhseQogICAgY3JlYXRlIDA2NjQgcm9vdCB1dG1wCiAgICAgICAgbWluc2l6ZSAxTQogICAgcm90YXRlIDEKfQoKL3Zhci9sb2cvYnRtcCB7CiAgICBtaXNzaW5nb2sKICAgIG1vbnRobHkKICAgIGNyZWF0ZSAwNjAwIHJvb3QgdXRtcAogICAgcm90YXRlIDEKfQpFT0YKCmVjaG8gImNvbmZpZ3VyaW5nIHl1bSByZXBvc2l0b3J5IGFuZCBydW5uaW5nIHl1bSB1cGRhdGUiCmNhdCA+L2V0Yy95dW0ucmVwb3MuZC9henVyZS5yZXBvIDw8J0VPRicKW2F6dXJlLWNsaV0KbmFtZT1henVyZS1jbGkKYmFzZXVybD1odHRwczovL3BhY2thZ2VzLm1pY3Jvc29mdC5jb20veXVtcmVwb3MvYXp1cmUtY2xpCmVuYWJsZWQ9eWVzCmdwZ2NoZWNrPXllcwoKW2F6dXJlY29yZV0KbmFtZT1henVyZWNvcmUKYmFzZXVybD1odHRwczovL3BhY2thZ2VzLm1pY3Jvc29mdC5jb20veXVtcmVwb3MvYXp1cmVjb3JlCmVuYWJsZWQ9eWVzCmdwZ2NoZWNrPW5vCkVPRgoKc2VtYW5hZ2UgZmNvbnRleHQgLWEgLXQgdmFyX2xvZ190ICIvdmFyL2xvZy9qb3VybmFsKC8uKik/Igpta2RpciAtcCAvdmFyL2xvZy9qb3VybmFsCgpmb3IgYXR0ZW1wdCBpbiB7MS4uNjB9OyBkbwp5dW0gLXkgaW5zdGFsbCBjbGFtYXYgYXpzZWMtY2xhbWF2IGF6c2VjLW1vbml0b3IgYXp1cmUtY2xpIGF6dXJlLW1kc2QgYXp1cmUtc2VjdXJpdHkgcG9kbWFuIHBvZG1hbi1kb2NrZXIgb3BlbnNzbC1wZXJsIHB5dGhvbjMgJiYgYnJlYWsKICAjIGhhY2sgLSB3ZSBhcmUgaW5zdGFsbGluZyBweXRob24zIG9uIGhvc3RzIGR1ZSB0byBhbiBpc3N1ZSB3aXRoIEF6dXJlIExpbnV4IEV4dGVuc2lvbnMgaHR0cHM6Ly9naXRodWIuY29tL0F6dXJlL2F6dXJlLWxpbnV4LWV4dGVuc2lvbnMvcHVsbC8xNTA1CiAgaWYgW1sgJHthdHRlbXB0fSAtbHQgNjAgXV07IHRoZW4gc2xlZXAgMzA7IGVsc2UgZXhpdCAxOyBmaQpkb25lCgojIGh0dHBzOi8vYWNjZXNzLnJlZGhhdC5jb20vc2VjdXJpdHkvY3ZlL2N2ZS0yMDIwLTEzNDAxCmVjaG8gImFwcGx5aW5nIGZpcmV3YWxsIHJ1bGVzIgpjYXQgPi9ldGMvc3lzY3RsLmQvMDItZGlzYWJsZS1hY2NlcHQtcmEuY29uZiA8PCdFT0YnCm5ldC5pcHY2LmNvbmYuYWxsLmFjY2VwdF9yYT0wCkVPRgoKY2F0ID4vZXRjL3N5c2N0bC5kLzAxLWRpc2FibGUtY29yZS5jb25mIDw8J0VPRicKa2VybmVsLmNvcmVfcGF0dGVybiA9IHwvYmluL3RydWUKRU9GCnN5c2N0bCAtLXN5c3RlbQoKZmlyZXdhbGwtY21kIC0tYWRkLXBvcnQ9NDQzL3RjcCAtLXBlcm1hbmVudApmaXJld2FsbC1jbWQgLS1hZGQtcG9ydD00NDQvdGNwIC0tcGVybWFuZW50CmZpcmV3YWxsLWNtZCAtLWFkZC1wb3J0PTIyMjIvdGNwIC0tcGVybWFuZW50CgpleHBvcnQgQVpVUkVfQ0xPVURfTkFNRT0kQVpVUkVDTE9VRE5BTUUKCmVjaG8gImxvZ2dpbmcgaW50byBwcm9kIGFjciIKYXogbG9naW4gLWkgLS1hbGxvdy1uby1zdWJzY3JpcHRpb25zCgojIFN1cHByZXNzIGVtdWxhdGlvbiBvdXRwdXQgZm9yIHBvZG1hbiBpbnN0ZWFkIG9mIGRvY2tlciBmb3IgYXogYWNyIGNvbXBhdGFiaWxpdHkKbWtkaXIgLXAgL2V0Yy9jb250YWluZXJzLwp0b3VjaCAvZXRjL2NvbnRhaW5lcnMvbm9kb2NrZXIKCm1rZGlyIC1wIC9yb290Ly5kb2NrZXIKUkVHSVNUUllfQVVUSF9GSUxFPS9yb290Ly5kb2NrZXIvY29uZmlnLmpzb24gYXogYWNyIGxvZ2luIC0tbmFtZSAiJChzZWQgLWUgJ3N8LiovfHwnIDw8PCIkQUNSUkVTT1VSQ0VJRCIpIgoKTURNSU1BR0U9IiR7UlBJTUFHRSUlLyp9LyR7TURNSU1BR0UjKi99Igpkb2NrZXIgcHVsbCAiJE1ETUlNQUdFIgpkb2NrZXIgcHVsbCAiJFJQSU1BR0UiCmRvY2tlciBwdWxsICIkRkxVRU5UQklUSU1BR0UiCgpheiBsb2dvdXQKCmVjaG8gImNvbmZpZ3VyaW5nIGZsdWVudGJpdCBzZXJ2aWNlIgpta2RpciAtcCAvZXRjL2ZsdWVudGJpdC8KbWtkaXIgLXAgL3Zhci9saWIvZmx1ZW50CgpjYXQgPi9ldGMvZmx1ZW50Yml0L2ZsdWVudGJpdC5jb25mIDw8J0VPRicKW0lOUFVUXQoJTmFtZSBzeXN0ZW1kCglUYWcgam91cm5hbGQKCVN5c3RlbWRfRmlsdGVyIF9DT01NPWFybwoJREIgL3Zhci9saWIvZmx1ZW50L2pvdXJuYWxkYgoKW0ZJTFRFUl0KCU5hbWUgbW9kaWZ5CglNYXRjaCBqb3VybmFsZAoJUmVtb3ZlX3dpbGRjYXJkIF8KCVJlbW92ZSBUSU1FU1RBTVAKCltGSUxURVJdCglOYW1lIHJld3JpdGVfdGFnCglNYXRjaCBqb3VybmFsZAoJUnVsZSAkTE9HS0lORCBhc3luY3FvcyBhc3luY3FvcyB0cnVlCgpbRklMVEVSXQoJTmFtZSBtb2RpZnkKCU1hdGNoIGFzeW5jcW9zCglSZW1vdmUgQ0xJRU5UX1BSSU5DSVBBTF9OQU1FCglSZW1vdmUgRklMRQoJUmVtb3ZlIENPTVBPTkVOVAoKW0ZJTFRFUl0KCU5hbWUgcmV3cml0ZV90YWcKCU1hdGNoIGpvdXJuYWxkCglSdWxlICRMT0dLSU5EIGlmeGF1ZGl0IGlmeGF1ZGl0IGZhbHNlCgpbT1VUUFVUXQoJTmFtZSBmb3J3YXJkCglNYXRjaCAqCglQb3J0IDI5MjMwCkVPRgoKZWNobyAiRkxVRU5UQklUSU1BR0U9JEZMVUVOVEJJVElNQUdFIiA+L2V0Yy9zeXNjb25maWcvZmx1ZW50Yml0CgpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vZmx1ZW50Yml0LnNlcnZpY2UgPDwnRU9GJwpbVW5pdF0KQWZ0ZXI9bmV0d29yay1vbmxpbmUudGFyZ2V0CldhbnRzPW5ldHdvcmstb25saW5lLnRhcmdldApTdGFydExpbWl0SW50ZXJ2YWxTZWM9MAoKW1NlcnZpY2VdClJlc3RhcnRTZWM9MXMKRW52aXJvbm1lbnRGaWxlPS9ldGMvc3lzY29uZmlnL2ZsdWVudGJpdApFeGVjU3RhcnRQcmU9LS91c3IvYmluL2RvY2tlciBybSAtZiAlTgpFeGVjU3RhcnQ9L3Vzci9iaW4vZG9ja2VyIHJ1biBcCiAgLS1zZWN1cml0eS1vcHQgbGFiZWw9ZGlzYWJsZSBcCiAgLS1lbnRyeXBvaW50IC9vcHQvdGQtYWdlbnQtYml0L2Jpbi90ZC1hZ2VudC1iaXQgXAogIC0tbmV0PWhvc3QgXAogIC0taG9zdG5hbWUgJUggXAogIC0tbmFtZSAlTiBcCiAgLS1ybSBcCiAgLS1jYXAtZHJvcCBuZXRfcmF3IFwKICAtdiAvZXRjL2ZsdWVudGJpdC9mbHVlbnRiaXQuY29uZjovZXRjL2ZsdWVudGJpdC9mbHVlbnRiaXQuY29uZiBcCiAgLXYgL3Zhci9saWIvZmx1ZW50Oi92YXIvbGliL2ZsdWVudDp6IFwKICAtdiAvdmFyL2xvZy9qb3VybmFsOi92YXIvbG9nL2pvdXJuYWw6cm8gXAogIC12IC9ldGMvbWFjaGluZS1pZDovZXRjL21hY2hpbmUtaWQ6cm8gXAogICRGTFVFTlRCSVRJTUFHRSBcCiAgLWMgL2V0Yy9mbHVlbnRiaXQvZmx1ZW50Yml0LmNvbmYKCkV4ZWNTdG9wPS91c3IvYmluL2RvY2tlciBzdG9wICVOClJlc3RhcnQ9YWx3YXlzClJlc3RhcnRTZWM9NQpTdGFydExpbWl0SW50ZXJ2YWw9MAoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIudGFyZ2V0CkVPRgoKbWtkaXIgL2V0Yy9hcm8tcnAKYmFzZTY0IC1kIDw8PCIkQURNSU5BUElDQUJVTkRMRSIgPi9ldGMvYXJvLXJwL2FkbWluLWNhLWJ1bmRsZS5wZW0KaWYgW1sgLW4gIiRBUk1BUElDQUJVTkRMRSIgXV07IHRoZW4KICBiYXNlNjQgLWQgPDw8IiRBUk1BUElDQUJVTkRMRSIgPi9ldGMvYXJvLXJwL2FybS1jYS1idW5kbGUucGVtCmZpCmNob3duIC1SIDEwMDA6MTAwMCAvZXRjL2Fyby1ycAoKZWNobyAiY29uZmlndXJpbmcgbWRtIHNlcnZpY2UiCmNhdCA+L2V0Yy9zeXNjb25maWcvbWRtIDw8RU9GCk1ETUZST05URU5EVVJMPSckTURNRlJPTlRFTkRVUkwnCk1ETUlNQUdFPSckTURNSU1BR0UnCk1ETVNPVVJDRUVOVklST05NRU5UPSckTE9DQVRJT04nCk1ETVNPVVJDRVJPTEU9cnAKTURNU09VUkNFUk9MRUlOU1RBTkNFPSckKGhvc3RuYW1lKScKRU9GCgpta2RpciAvdmFyL2V0dwpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vbWRtLnNlcnZpY2UgPDwnRU9GJwpbVW5pdF0KQWZ0ZXI9bmV0d29yay1vbmxpbmUudGFyZ2V0CldhbnRzPW5ldHdvcmstb25saW5lLnRhcmdldAoKW1NlcnZpY2VdCkVudmlyb25tZW50RmlsZT0vZXRjL3N5c2NvbmZpZy9tZG0KRXhlY1N0YXJ0UHJlPS0vdXNyL2Jpbi9kb2NrZXIgcm0gLWYgJU4KRXhlY1N0YXJ0PS91c3IvYmluL2RvY2tlciBydW4gXAogIC0tZW50cnlwb2ludCAvdXNyL3NiaW4vTWV0cmljc0V4dGVuc2lvbiBcCiAgLS1ob3N0bmFtZSAlSCBcCiAgLS1uYW1lICVOIFwKICAtLXJtIFwKICAtLWNhcC1kcm9wIG5ldF9yYXcgXAogIC1tIDJnIFwKICAtdiAvZXRjL21kbS5wZW06L2V0Yy9tZG0ucGVtIFwKICAtdiAvdmFyL2V0dzovdmFyL2V0dzp6IFwKICAkTURNSU1BR0UgXAogIC1DZXJ0RmlsZSAvZXRjL21kbS5wZW0gXAogIC1Gcm9udEVuZFVybCAkTURNRlJPTlRFTkRVUkwgXAogIC1Mb2dnZXIgQ29uc29sZSBcCiAgLUxvZ0xldmVsIFdhcm5pbmcgXAogIC1Qcml2YXRlS2V5RmlsZSAvZXRjL21kbS5wZW0gXAogIC1Tb3VyY2VFbnZpcm9ubWVudCAkTURNU09VUkNFRU5WSVJPTk1FTlQgXAogIC1Tb3VyY2VSb2xlICRNRE1TT1VSQ0VST0xFIFwKICAtU291cmNlUm9sZUluc3RhbmNlICRNRE1TT1VSQ0VST0xFSU5TVEFOQ0UKRXhlY1N0b3A9L3Vzci9iaW4vZG9ja2VyIHN0b3AgJU4KUmVzdGFydD1hbHdheXMKUmVzdGFydFNlYz0xClN0YXJ0TGltaXRJbnRlcnZhbD0wCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQKRU9GCgplY2hvICJjb25maWd1cmluZyBhcm8tcnAgc2VydmljZSIKY2F0ID4vZXRjL3N5c2NvbmZpZy9hcm8tcnAgPDxFT0YKQUNSX1JFU09VUkNFX0lEPSckQUNSUkVTT1VSQ0VJRCcKQURNSU5fQVBJX0NMSUVOVF9DRVJUX0NPTU1PTl9OQU1FPSckQURNSU5BUElDTElFTlRDRVJUQ09NTU9OTkFNRScKQVJNX0FQSV9DTElFTlRfQ0VSVF9DT01NT05fTkFNRT0nJEFSTUFQSUNMSUVOVENFUlRDT01NT05OQU1FJwpBWlVSRV9BUk1fQ0xJRU5UX0lEPSckQVJNQ0xJRU5USUQnCkFaVVJFX0ZQX0NMSUVOVF9JRD0nJEZQQ0xJRU5USUQnCkFaVVJFX0ZQX1NFUlZJQ0VfUFJJTkNJUEFMX0lEPSckRlBTRVJWSUNFUFJJTkNJUEFMSUQnCkNMVVNURVJfTURNX0FDQ09VTlQ9JyRDTFVTVEVSTURNQUNDT1VOVCcKQ0xVU1RFUl9NRE1fTkFNRVNQQUNFPVJQCkNMVVNURVJfTURTRF9BQ0NPVU5UPSckQ0xVU1RFUk1EU0RBQ0NPVU5UJwpDTFVTVEVSX01EU0RfQ09ORklHX1ZFUlNJT049JyRDTFVTVEVSTURTRENPTkZJR1ZFUlNJT04nCkNMVVNURVJfTURTRF9OQU1FU1BBQ0U9JyRDTFVTVEVSTURTRE5BTUVTUEFDRScKREFUQUJBU0VfQUNDT1VOVF9OQU1FPSckREFUQUJBU0VBQ0NPVU5UTkFNRScKRE9NQUlOX05BTUU9JyRMT0NBVElPTi4kQ0xVU1RFUlBBUkVOVERPTUFJTk5BTUUnCkdBVEVXQVlfRE9NQUlOUz0nJEdBVEVXQVlET01BSU5TJwpHQVRFV0FZX1JFU09VUkNFR1JPVVA9JyRHQVRFV0FZUkVTT1VSQ0VHUk9VUE5BTUUnCktFWVZBVUxUX1BSRUZJWD0nJEtFWVZBVUxUUFJFRklYJwpNRE1fQUNDT1VOVD0nJFJQTURNQUNDT1VOVCcKTURNX05BTUVTUEFDRT1SUApNRFNEX0VOVklST05NRU5UPSckTURTREVOVklST05NRU5UJwpSUF9GRUFUVVJFUz0nJFJQRkVBVFVSRVMnClJQSU1BR0U9JyRSUElNQUdFJwpBUk9fSU5TVEFMTF9WSUFfSElWRT0nJENMVVNURVJTSU5TVEFMTFZJQUhJVkUnCkFST19ISVZFX0RFRkFVTFRfSU5TVEFMTEVSX1BVTExTUEVDPSckQ0xVU1RFUkRFRkFVTFRJTlNUQUxMRVJQVUxMU1BFQycKQVJPX0FET1BUX0JZX0hJVkU9JyRDTFVTVEVSU0FET1BUQllISVZFJwpVU0VfQ0hFQ0tBQ0NFU1M9JyRVU0VDSEVDS0FDQ0VTUycKT0lEQ19BRkRfRU5EUE9JTlQ9JyRMT0NBVElPTi5vaWMuJFJQUEFSRU5URE9NQUlOTkFNRScKT0lEQ19TVE9SQUdFX0FDQ09VTlRfTkFNRT0nJE9JRENTVE9SQUdFQUNDT1VOVE5BTUUnCkVPRgoKY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL2Fyby1ycC5zZXJ2aWNlIDw8J0VPRicKW1VuaXRdCkFmdGVyPW5ldHdvcmstb25saW5lLnRhcmdldApXYW50cz1uZXR3b3JrLW9ubGluZS50YXJnZXQKCltTZXJ2aWNlXQpFbnZpcm9ubWVudEZpbGU9L2V0Yy9zeXNjb25maWcvYXJvLXJwCkV4ZWNTdGFydFByZT0tL3Vzci9iaW4vZG9ja2VyIHJtIC1mICVOCkV4ZWNTdGFydD0vdXNyL2Jpbi9kb2NrZXIgcnVuIFwKICAtLWhvc3RuYW1lICVIIFwKICAtLW5hbWUgJU4gXAogIC0tcm0gXAogIC0tY2FwLWRyb3AgbmV0X3JhdyBcCiAgLWUgQUNSX1JFU09VUkNFX0lEIFwKICAtZSBBRE1JTl9BUElfQ0xJRU5UX0NFUlRfQ09NTU9OX05BTUUgXAogIC1lIEFSTV9BUElfQ0xJRU5UX0NFUlRfQ09NTU9OX05BTUUgXAogIC1lIEFaVVJFX0FSTV9DTElFTlRfSUQgXAogIC1lIEFaVVJFX0ZQX0NMSUVOVF9JRCBcCiAgLWUgQ0xVU1RFUl9NRE1fQUNDT1VOVCBcCiAgLWUgQ0xVU1RFUl9NRE1fTkFNRVNQQUNFIFwKICAtZSBDTFVTVEVSX01EU0RfQUNDT1VOVCBcCiAgLWUgQ0xVU1RFUl9NRFNEX0NPTkZJR19WRVJTSU9OIFwKICAtZSBDTFVTVEVSX01EU0RfTkFNRVNQQUNFIFwKICAtZSBEQVRBQkFTRV9BQ0NPVU5UX05BTUUgXAogIC1lIERPTUFJTl9OQU1FIFwKICAtZSBHQVRFV0FZX0RPTUFJTlMgXAogIC1lIEdBVEVXQVlfUkVTT1VSQ0VHUk9VUCBcCiAgLWUgS0VZVkFVTFRfUFJFRklYIFwKICAtZSBNRE1fQUNDT1VOVCBcCiAgLWUgTURNX05BTUVTUEFDRSBcCiAgLWUgTURTRF9FTlZJUk9OTUVOVCBcCiAgLWUgUlBfRkVBVFVSRVMgXAogIC1lIEFST19JTlNUQUxMX1ZJQV9ISVZFIFwKICAtZSBBUk9fSElWRV9ERUZBVUxUX0lOU1RBTExFUl9QVUxMU1BFQyBcCiAgLWUgQVJPX0FET1BUX0JZX0hJVkUgXAogIC1lIFVTRV9DSEVDS0FDQ0VTUyBcCiAgLWUgT0lEQ19BRkRfRU5EUE9JTlQgXAogIC1lIE9JRENfU1RPUkFHRV9BQ0NPVU5UX05BTUUgXAogIC1tIDJnIFwKICAtcCA0NDM6ODQ0MyBcCiAgLXYgL2V0Yy9hcm8tcnA6L2V0Yy9hcm8tcnAgXAogIC12IC9ydW4vc3lzdGVtZC9qb3VybmFsOi9ydW4vc3lzdGVtZC9qb3VybmFsIFwKICAtdiAvdmFyL2V0dzovdmFyL2V0dzp6IFwKICAkUlBJTUFHRSBcCiAgcnAKRXhlY1N0b3A9L3Vzci9iaW4vZG9ja2VyIHN0b3AgLXQgMzYwMCAlTgpUaW1lb3V0U3RvcFNlYz0zNjAwClJlc3RhcnQ9YWx3YXlzClJlc3RhcnRTZWM9MQpTdGFydExpbWl0SW50ZXJ2YWw9MAoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIudGFyZ2V0CkVPRgoKIyBET01BSU5fTkFNRSwgQ0xVU1RFUl9NRFNEX0FDQ09VTlQsIENMVVNURVJfTURTRF9DT05GSUdfVkVSU0lPTiwgR0FURVdBWV9ET01BSU5TLCBHQVRFV0FZX1JFU09VUkNFR1JPVVAsIE1EU0RfRU5WSVJPTk1FTlQgQ0xVU1RFUl9NRFNEX05BTUVTUEFDRQojIGFyZSBub3QgdXNlZCwgYnV0IGNhbid0IGVhc2lseSBiZSByZWZhY3RvcmVkIG91dC4gU2hvdWxkIGJlIHJldmlzaXRlZCBpbiB0aGUgZnV0dXJlLgplY2hvICJjb25maWd1cmluZyBhcm8tbW9uaXRvciBzZXJ2aWNlIgpjYXQgPi9ldGMvc3lzY29uZmlnL2Fyby1tb25pdG9yIDw8RU9GCkFaVVJFX0ZQX0NMSUVOVF9JRD0nJEZQQ0xJRU5USUQnCkRPTUFJTl9OQU1FPSckTE9DQVRJT04uJENMVVNURVJQQVJFTlRET01BSU5OQU1FJwpDTFVTVEVSX01EU0RfQUNDT1VOVD0nJENMVVNURVJNRFNEQUNDT1VOVCcKQ0xVU1RFUl9NRFNEX0NPTkZJR19WRVJTSU9OPSckQ0xVU1RFUk1EU0RDT05GSUdWRVJTSU9OJwpHQVRFV0FZX0RPTUFJTlM9JyRHQVRFV0FZRE9NQUlOUycKR0FURVdBWV9SRVNPVVJDRUdST1VQPSckR0FURVdBWVJFU09VUkNFR1JPVVBOQU1FJwpNRFNEX0VOVklST05NRU5UPSckTURTREVOVklST05NRU5UJwpDTFVTVEVSX01EU0RfTkFNRVNQQUNFPSckQ0xVU1RFUk1EU0ROQU1FU1BBQ0UnCkNMVVNURVJfTURNX0FDQ09VTlQ9JyRDTFVTVEVSTURNQUNDT1VOVCcKQ0xVU1RFUl9NRE1fTkFNRVNQQUNFPUJCTQpEQVRBQkFTRV9BQ0NPVU5UX05BTUU9JyREQVRBQkFTRUFDQ09VTlROQU1FJwpLRVlWQVVMVF9QUkVGSVg9JyRLRVlWQVVMVFBSRUZJWCcKTURNX0FDQ09VTlQ9JyRSUE1ETUFDQ09VTlQnCk1ETV9OQU1FU1BBQ0U9QkJNClJQSU1BR0U9JyRSUElNQUdFJwpFT0YKCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9hcm8tbW9uaXRvci5zZXJ2aWNlIDw8J0VPRicKW1VuaXRdCkFmdGVyPW5ldHdvcmstb25saW5lLnRhcmdldApXYW50cz1uZXR3b3JrLW9ubGluZS50YXJnZXQKCltTZXJ2aWNlXQpFbnZpcm9ubWVudEZpbGU9L2V0Yy9zeXNjb25maWcvYXJvLW1vbml0b3IKRXhlY1N0YXJ0UHJlPS0vdXNyL2Jpbi9kb2NrZXIgcm0gLWYgJU4KRXhlY1N0YXJ0PS91c3IvYmluL2RvY2tlciBydW4gXAogIC0taG9zdG5hbWUgJUggXAogIC0tbmFtZSAlTiBcCiAgLS1ybSBcCiAgLS1jYXAtZHJvcCBuZXRfcmF3IFwKICAtZSBBWlVSRV9GUF9DTElFTlRfSUQgXAogIC1lIERPTUFJTl9OQU1FIFwKICAtZSBDTFVTVEVSX01EU0RfQUNDT1VOVCBcCiAgLWUgQ0xVU1RFUl9NRFNEX0NPTkZJR19WRVJTSU9OIFwKICAtZSBHQVRFV0FZX0RPTUFJTlMgXAogIC1lIEdBVEVXQVlfUkVTT1VSQ0VHUk9VUCBcCiAgLWUgTURTRF9FTlZJUk9OTUVOVCBcCiAgLWUgQ0xVU1RFUl9NRFNEX05BTUVTUEFDRSBcCiAgLWUgQ0xVU1RFUl9NRE1fQUNDT1VOVCBcCiAgLWUgQ0xVU1RFUl9NRE1fTkFNRVNQQUNFIFwKICAtZSBEQVRBQkFTRV9BQ0NPVU5UX05BTUUgXAogIC1lIEtFWVZBVUxUX1BSRUZJWCBcCiAgLWUgTURNX0FDQ09VTlQgXAogIC1lIE1ETV9OQU1FU1BBQ0UgXAogIC1tIDIuNWcgXAogIC12IC9ydW4vc3lzdGVtZC9qb3VybmFsOi9ydW4vc3lzdGVtZC9qb3VybmFsIFwKICAtdiAvdmFyL2V0dzovdmFyL2V0dzp6IFwKICAkUlBJTUFHRSBcCiAgbW9uaXRvcgpSZXN0YXJ0PWFsd2F5cwpSZXN0YXJ0U2VjPTEKU3RhcnRMaW1pdEludGVydmFsPTAKCltJbnN0YWxsXQpXYW50ZWRCeT1tdWx0aS11c2VyLnRhcmdldApFT0YKCmVjaG8gImNvbmZpZ3VyaW5nIGFyby1wb3J0YWwgc2VydmljZSIKY2F0ID4vZXRjL3N5c2NvbmZpZy9hcm8tcG9ydGFsIDw8RU9GCkFaVVJFX1BPUlRBTF9BQ0NFU1NfR1JPVVBfSURTPSckUE9SVEFMQUNDRVNTR1JPVVBJRFMnCkFaVVJFX1BPUlRBTF9DTElFTlRfSUQ9JyRQT1JUQUxDTElFTlRJRCcKQVpVUkVfUE9SVEFMX0VMRVZBVEVEX0dST1VQX0lEUz0nJFBPUlRBTEVMRVZBVEVER1JPVVBJRFMnCkRBVEFCQVNFX0FDQ09VTlRfTkFNRT0nJERBVEFCQVNFQUNDT1VOVE5BTUUnCktFWVZBVUxUX1BSRUZJWD0nJEtFWVZBVUxUUFJFRklYJwpNRE1fQUNDT1VOVD0nJFJQTURNQUNDT1VOVCcKTURNX05BTUVTUEFDRT1Qb3J0YWwKUE9SVEFMX0hPU1ROQU1FPSckTE9DQVRJT04uYWRtaW4uJFJQUEFSRU5URE9NQUlOTkFNRScKUlBJTUFHRT0nJFJQSU1BR0UnCkVPRgoKY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL2Fyby1wb3J0YWwuc2VydmljZSA8PCdFT0YnCltVbml0XQpBZnRlcj1uZXR3b3JrLW9ubGluZS50YXJnZXQKV2FudHM9bmV0d29yay1vbmxpbmUudGFyZ2V0ClN0YXJ0TGltaXRJbnRlcnZhbD0wCgpbU2VydmljZV0KRW52aXJvbm1lbnRGaWxlPS9ldGMvc3lzY29uZmlnL2Fyby1wb3J0YWwKRXhlY1N0YXJ0UHJlPS0vdXNyL2Jpbi9kb2NrZXIgcm0gLWYgJU4KRXhlY1N0YXJ0PS91c3IvYmluL2RvY2tlciBydW4gXAogIC0taG9zdG5hbWUgJUggXAogIC0tbmFtZSAlTiBcCiAgLS1ybSBcCiAgLS1jYXAtZHJvcCBuZXRfcmF3IFwKICAtZSBBWlVSRV9QT1JUQUxfQUNDRVNTX0dST1VQX0lEUyBcCiAgLWUgQVpVUkVfUE9SVEFMX0NMSUVOVF9JRCBcCiAgLWUgQVpVUkVfUE9SVEFMX0VMRVZBVEVEX0dST1VQX0lEUyBcCiAgLWUgREFUQUJBU0VfQUNDT1VOVF9OQU1FIFwKICAtZSBLRVlWQVVMVF9QUkVGSVggXAogIC1lIE1ETV9BQ0NPVU5UIFwKICAtZSBNRE1fTkFNRVNQQUNFIFwKICAtZSBQT1JUQUxfSE9TVE5BTUUgXAogIC1tIDJnIFwKICAtcCA0NDQ6ODQ0NCBcCiAgLXAgMjIyMjoyMjIyIFwKICAtdiAvcnVuL3N5c3RlbWQvam91cm5hbDovcnVuL3N5c3RlbWQvam91cm5hbCBcCiAgLXYgL3Zhci9ldHc6L3Zhci9ldHc6eiBcCiAgJFJQSU1BR0UgXAogIHBvcnRhbApSZXN0YXJ0PWFsd2F5cwpSZXN0YXJ0U2VjPTEKCltJbnN0YWxsXQpXYW50ZWRCeT1tdWx0aS11c2VyLnRhcmdldApFT0YKCmVjaG8gImNvbmZpZ3VyaW5nIG1kc2QgYW5kIG1kbSBzZXJ2aWNlcyIKY2hjb24gLVIgc3lzdGVtX3U6b2JqZWN0X3I6dmFyX2xvZ190OnMwIC92YXIvb3B0L21pY3Jvc29mdC9saW51eG1vbmFnZW50Cgpta2RpciAtcCAvdmFyL2xpYi93YWFnZW50L01pY3Jvc29mdC5BenVyZS5LZXlWYXVsdC5TdG9yZQoKZm9yIHZhciBpbiAibWRzZCIgIm1kbSI7IGRvCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9kb3dubG9hZC0kdmFyLWNyZWRlbnRpYWxzLnNlcnZpY2UgPDxFT0YKW1VuaXRdCkRlc2NyaXB0aW9uPVBlcmlvZGljICR2YXIgY3JlZGVudGlhbHMgcmVmcmVzaAoKW1NlcnZpY2VdClR5cGU9b25lc2hvdApFeGVjU3RhcnQ9L3Vzci9sb2NhbC9iaW4vZG93bmxvYWQtY3JlZGVudGlhbHMuc2ggJHZhcgpFT0YKCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9kb3dubG9hZC0kdmFyLWNyZWRlbnRpYWxzLnRpbWVyIDw8RU9GCltVbml0XQpEZXNjcmlwdGlvbj1QZXJpb2RpYyAkdmFyIGNyZWRlbnRpYWxzIHJlZnJlc2gKQWZ0ZXI9bmV0d29yay1vbmxpbmUudGFyZ2V0CldhbnRzPW5ldHdvcmstb25saW5lLnRhcmdldAoKW1RpbWVyXQpPbkJvb3RTZWM9MG1pbgpPbkNhbGVuZGFyPTAvMTI6MDA6MDAKQWNjdXJhY3lTZWM9NXMKCltJbnN0YWxsXQpXYW50ZWRCeT10aW1lcnMudGFyZ2V0CkVPRgpkb25lCgpjYXQgPi91c3IvbG9jYWwvYmluL2Rvd25sb2FkLWNyZWRlbnRpYWxzLnNoIDw8RU9GCiMhL2Jpbi9iYXNoCnNldCAtZXUKCkNPTVBPTkVOVD0iXCQxIgplY2hvICJEb3dubG9hZCBcJENPTVBPTkVOVCBjcmVkZW50aWFscyIKClRFTVBfRElSPVwkKG1rdGVtcCAtZCkKZXhwb3J0IEFaVVJFX0NPTkZJR19ESVI9XCQobWt0ZW1wIC1kKQoKZWNobyAiTG9nZ2luZyBpbnRvIEF6dXJlLi4uIgpSRVRSSUVTPTMKd2hpbGUgWyAiXCRSRVRSSUVTIiAtZ3QgMCBdOyBkbwogICAgaWYgYXogbG9naW4gLWkgLS1hbGxvdy1uby1zdWJzY3JpcHRpb25zCiAgICB0aGVuCiAgICAgICAgZWNobyAiYXogbG9naW4gc3VjY2Vzc2Z1bCIKICAgICAgICBicmVhawogICAgZWxzZQogICAgICAgIGVjaG8gImF6IGxvZ2luIGZhaWxlZC4gUmV0cnlpbmcuLi4iCiAgICAgICAgbGV0IFJFVFJJRVMtPTEKICAgICAgICBzbGVlcCA1CiAgICBmaQpkb25lCgp0cmFwICJjbGVhbnVwIiBFWElUCgpjbGVhbnVwKCkgewogIGF6IGxvZ291dAogIFtbICJcJFRFTVBfRElSIiA9fiAvdG1wLy4rIF1dICYmIHJtIC1yZiBcJFRFTVBfRElSCiAgW1sgIlwkQVpVUkVfQ09ORklHX0RJUiIgPX4gL3RtcC8uKyBdXSAmJiBybSAtcmYgXCRBWlVSRV9DT05GSUdfRElSCn0KCmlmIFsgIlwkQ09NUE9ORU5UIiA9ICJtZG0iIF07IHRoZW4KICBDVVJSRU5UX0NFUlRfRklMRT0iL2V0Yy9tZG0ucGVtIgplbGlmIFsgIlwkQ09NUE9ORU5UIiA9ICJtZHNkIiBdOyB0aGVuCiAgQ1VSUkVOVF9DRVJUX0ZJTEU9Ii92YXIvbGliL3dhYWdlbnQvTWljcm9zb2Z0LkF6dXJlLktleVZhdWx0LlN0b3JlL21kc2QucGVtIgplbHNlCiAgZWNobyBJbnZhbGlkIHVzYWdlICYmIGV4aXQgMQpmaQoKU0VDUkVUX05BTUU9InJwLVwke0NPTVBPTkVOVH0iCk5FV19DRVJUX0ZJTEU9IlwkVEVNUF9ESVIvXCRDT01QT05FTlQucGVtIgpmb3IgYXR0ZW1wdCBpbiB7MS4uNX07IGRvCiAgYXoga2V5dmF1bHQgc2VjcmV0IGRvd25sb2FkIC0tZmlsZSBcJE5FV19DRVJUX0ZJTEUgLS1pZCAiaHR0cHM6Ly8kS0VZVkFVTFRQUkVGSVgtc3ZjLiRLRVlWQVVMVEROU1NVRkZJWC9zZWNyZXRzL1wkU0VDUkVUX05BTUUiICYmIGJyZWFrCiAgaWYgW1sgXCRhdHRlbXB0IC1sdCA1IF1dOyB0aGVuIHNsZWVwIDEwOyBlbHNlIGV4aXQgMTsgZmkKZG9uZQoKaWYgWyAtZiBcJE5FV19DRVJUX0ZJTEUgXTsgdGhlbgogIGlmIFsgIlwkQ09NUE9ORU5UIiA9ICJtZHNkIiBdOyB0aGVuCiAgICBjaG93biBzeXNsb2c6c3lzbG9nIFwkTkVXX0NFUlRfRklMRQogIGVsc2UKICAgIHNlZCAtaSAtbmUgJzEsL0VORCBDRVJUSUZJQ0FURS8gcCcgXCRORVdfQ0VSVF9GSUxFCiAgZmkKCiAgbmV3X2NlcnRfc249IlwkKG9wZW5zc2wgeDUwOSAtaW4gIlwkTkVXX0NFUlRfRklMRSIgLW5vb3V0IC1zZXJpYWwgfCBhd2sgLUY9ICd7cHJpbnQgXCQyfScpIgogIGN1cnJlbnRfY2VydF9zbj0iXCQob3BlbnNzbCB4NTA5IC1pbiAiXCRDVVJSRU5UX0NFUlRfRklMRSIgLW5vb3V0IC1zZXJpYWwgfCBhd2sgLUY9ICd7cHJpbnQgXCQyfScpIgogIGlmIFtbICEgLXogXCRuZXdfY2VydF9zbiBdXSAmJiBbWyBcJG5ld19jZXJ0X3NuICE9ICJcJGN1cnJlbnRfY2VydF9zbiIgXV07IHRoZW4KICAgIGVjaG8gdXBkYXRpbmcgY2VydGlmaWNhdGUgZm9yIFwkQ09NUE9ORU5UCiAgICBjaG1vZCAwNjAwIFwkTkVXX0NFUlRfRklMRQogICAgbXYgXCRORVdfQ0VSVF9GSUxFIFwkQ1VSUkVOVF9DRVJUX0ZJTEUKICBmaQplbHNlCiAgZWNobyBGYWlsZWQgdG8gcmVmcmVzaCBjZXJ0aWZpY2F0ZSBmb3IgXCRDT01QT05FTlQgJiYgZXhpdCAxCmZpCkVPRgoKY2htb2QgdSt4IC91c3IvbG9jYWwvYmluL2Rvd25sb2FkLWNyZWRlbnRpYWxzLnNoCgpzeXN0ZW1jdGwgZW5hYmxlIGRvd25sb2FkLW1kc2QtY3JlZGVudGlhbHMudGltZXIKc3lzdGVtY3RsIGVuYWJsZSBkb3dubG9hZC1tZG0tY3JlZGVudGlhbHMudGltZXIKCi91c3IvbG9jYWwvYmluL2Rvd25sb2FkLWNyZWRlbnRpYWxzLnNoIG1kc2QKL3Vzci9sb2NhbC9iaW4vZG93bmxvYWQtY3JlZGVudGlhbHMuc2ggbWRtCk1EU0RDRVJUSUZJQ0FURVNBTj0kKG9wZW5zc2wgeDUwOSAtaW4gL3Zhci9saWIvd2FhZ2VudC9NaWNyb3NvZnQuQXp1cmUuS2V5VmF1bHQuU3RvcmUvbWRzZC5wZW0gLW5vb3V0IC1zdWJqZWN0IHwgc2VkIC1lICdzLy4qQ04gPSAvLycpCgpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vd2F0Y2gtbWRtLWNyZWRlbnRpYWxzLnNlcnZpY2UgPDxFT0YKW1VuaXRdCkRlc2NyaXB0aW9uPVdhdGNoIGZvciBjaGFuZ2VzIGluIG1kbS5wZW0gYW5kIHJlc3RhcnRzIHRoZSBtZG0gc2VydmljZQoKW1NlcnZpY2VdClR5cGU9b25lc2hvdApFeGVjU3RhcnQ9L3Vzci9iaW4vc3lzdGVtY3RsIHJlc3RhcnQgbWRtLnNlcnZpY2UKCltJbnN0YWxsXQpXYW50ZWRCeT1tdWx0aS11c2VyLnRhcmdldApFT0YKCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS93YXRjaC1tZG0tY3JlZGVudGlhbHMucGF0aCA8PEVPRgpbUGF0aF0KUGF0aE1vZGlmaWVkPS9ldGMvbWRtLnBlbQoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIudGFyZ2V0CkVPRgoKc3lzdGVtY3RsIGVuYWJsZSB3YXRjaC1tZG0tY3JlZGVudGlhbHMucGF0aApzeXN0ZW1jdGwgc3RhcnQgd2F0Y2gtbWRtLWNyZWRlbnRpYWxzLnBhdGgKCm1rZGlyIC9ldGMvc3lzdGVtZC9zeXN0ZW0vbWRzZC5zZXJ2aWNlLmQKY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL21kc2Quc2VydmljZS5kL292ZXJyaWRlLmNvbmYgPDwnRU9GJwpbVW5pdF0KQWZ0ZXI9bmV0d29yay1vbmxpbmUudGFyZ2V0CkVPRgoKY2F0ID4vZXRjL2RlZmF1bHQvbWRzZCA8PEVPRgpNRFNEX1JPTEVfUFJFRklYPS92YXIvcnVuL21kc2QvZGVmYXVsdApNRFNEX09QVElPTlM9Ii1BIC1kIC1yIFwkTURTRF9ST0xFX1BSRUZJWCIKCmV4cG9ydCBNT05JVE9SSU5HX0dDU19FTlZJUk9OTUVOVD0nJE1EU0RFTlZJUk9OTUVOVCcKZXhwb3J0IE1PTklUT1JJTkdfR0NTX0FDQ09VTlQ9JyRSUE1EU0RBQ0NPVU5UJwpleHBvcnQgTU9OSVRPUklOR19HQ1NfUkVHSU9OPSckTE9DQVRJT04nCmV4cG9ydCBNT05JVE9SSU5HX0dDU19BVVRIX0lEX1RZUEU9QXV0aEtleVZhdWx0CmV4cG9ydCBNT05JVE9SSU5HX0dDU19BVVRIX0lEPSckTURTRENFUlRJRklDQVRFU0FOJwpleHBvcnQgTU9OSVRPUklOR19HQ1NfTkFNRVNQQUNFPSckUlBNRFNETkFNRVNQQUNFJwpleHBvcnQgTU9OSVRPUklOR19DT05GSUdfVkVSU0lPTj0nJFJQTURTRENPTkZJR1ZFUlNJT04nCmV4cG9ydCBNT05JVE9SSU5HX1VTRV9HRU5FVkFfQ09ORklHX1NFUlZJQ0U9dHJ1ZQoKZXhwb3J0IE1PTklUT1JJTkdfVEVOQU5UPSckTE9DQVRJT04nCmV4cG9ydCBNT05JVE9SSU5HX1JPTEU9cnAKZXhwb3J0IE1PTklUT1JJTkdfUk9MRV9JTlNUQU5DRT0nJChob3N0bmFtZSknCgpleHBvcnQgTURTRF9NU0dQQUNLX1NPUlRfQ09MVU1OUz0xCkVPRgoKIyBzZXR0aW5nIE1PTklUT1JJTkdfR0NTX0FVVEhfSURfVFlQRT1BdXRoS2V5VmF1bHQgc2VlbXMgdG8gaGF2ZSBjYXVzZWQgbWRzZCBub3QKIyB0byBob25vdXIgU1NMX0NFUlRfRklMRSBhbnkgbW9yZSwgaGVhdmVuIG9ubHkga25vd3Mgd2h5Lgpta2RpciAtcCAvdXNyL2xpYi9zc2wvY2VydHMKY3NwbGl0IC1mIC91c3IvbGliL3NzbC9jZXJ0cy9jZXJ0LSAtYiAlMDNkLnBlbSAvZXRjL3BraS90bHMvY2VydHMvY2EtYnVuZGxlLmNydCAvXiQvMSB7Kn0gPi9kZXYvbnVsbApjX3JlaGFzaCAvdXNyL2xpYi9zc2wvY2VydHMKCiMgd2UgbGVhdmUgY2xpZW50SWQgYmxhbmsgYXMgbG9uZyBhcyBvbmx5IDEgbWFuYWdlZCBpZGVudGl0eSBhc3NpZ25lZCB0byB2bXNzCiMgaWYgd2UgaGF2ZSBtb3JlIHRoYW4gMSwgd2Ugd2lsbCBuZWVkIHRvIHBvcHVsYXRlIHdpdGggY2xpZW50SWQgdXNlZCBmb3Igb2ZmLW5vZGUgc2Nhbm5pbmcKY2F0ID4vZXRjL2RlZmF1bHQvdnNhLW5vZGVzY2FuLWFnZW50LmNvbmZpZyA8PEVPRgp7CiAgICAiTmljZSI6IDE5LAogICAgIlRpbWVvdXQiOiAxMDgwMCwKICAgICJDbGllbnRJZCI6ICIiLAogICAgIlRlbmFudElkIjogIiRBWlVSRVNFQ1BBQ0tWU0FURU5BTlRJRCIsCiAgICAiUXVhbHlzU3RvcmVCYXNlVXJsIjogIiRBWlVSRVNFQ1BBQ0tRVUFMWVNVUkwiLAogICAgIlByb2Nlc3NUaW1lb3V0IjogMzAwLAogICAgIkNvbW1hbmREZWxheSI6IDAKICB9CkVPRgoKZWNobyAiZW5hYmxpbmcgYXJvIHNlcnZpY2VzIgpmb3Igc2VydmljZSBpbiBhcm8tbW9uaXRvciBhcm8tcG9ydGFsIGFyby1ycCBhdW9tcyBhenNlY2QgYXpzZWNtb25kIG1kc2QgbWRtIGNocm9ueWQgZmx1ZW50Yml0OyBkbwogIHN5c3RlbWN0bCBlbmFibGUgJHNlcnZpY2Uuc2VydmljZQpkb25lCgpmb3Igc2NhbiBpbiBiYXNlbGluZSBjbGFtYXYgc29mdHdhcmU7IGRvCiAgL3Vzci9sb2NhbC9iaW4vYXpzZWNkIGNvbmZpZyAtcyAkc2NhbiAtZCBQMUQKZG9uZQoKZWNobyAicmVib290aW5nIgpyZXN0b3JlY29uIC1SRiAvdmFyL2xvZy8qCihzbGVlcCAzMDsgcmVib290KSAmCg==')))]"
                                     }
                                 }
                             }
diff --git a/pkg/deploy/generator/scripts/rpVMSS.sh b/pkg/deploy/generator/scripts/rpVMSS.sh
index 7351af958..545d124f3 100644
--- a/pkg/deploy/generator/scripts/rpVMSS.sh
+++ b/pkg/deploy/generator/scripts/rpVMSS.sh
@@ -169,18 +169,6 @@ cat >/etc/fluentbit/fluentbit.conf <<'EOF'
 	Match journald
 	Rule $LOGKIND ifxaudit ifxaudit false
 
-[FILTER]
-	Name rewrite_tag
-	Match journald
-	Rule $LOGKIND outboundRequests outboundRequests false
-
-[FILTER]
-	Name modify
-	Match  outboundRequests
-	Remove CLIENT_PRINCIPAL_NAME
-	Remove FILE
-	Remove COMPONENT
-
 [OUTPUT]
 	Name forward
 	Match *
diff --git a/pkg/frontend/middleware/log.go b/pkg/frontend/middleware/log.go
index 789d891d8..801d50547 100644
--- a/pkg/frontend/middleware/log.go
+++ b/pkg/frontend/middleware/log.go
@@ -17,6 +17,7 @@ import (
 	"github.com/Azure/ARO-RP/pkg/api/admin"
 	utillog "github.com/Azure/ARO-RP/pkg/util/log"
 	"github.com/Azure/ARO-RP/pkg/util/log/audit"
+	"github.com/Azure/ARO-RP/pkg/util/uuid"
 )
 
 type logResponseWriter struct {
@@ -64,8 +65,12 @@ func (l LogMiddleware) Log(h http.Handler) http.Handler {
 		r.Body = &logReadCloser{ReadCloser: r.Body}
 		w = &logResponseWriter{ResponseWriter: w, statusCode: http.StatusOK}
 
-		correlationData := api.CreateCorrelationDataFromReq(r)
-		correlationData.RequestTime = t
+		correlationData := &api.CorrelationData{
+			ClientRequestID: r.Header.Get("X-Ms-Client-Request-Id"),
+			CorrelationID:   r.Header.Get("X-Ms-Correlation-Request-Id"),
+			RequestID:       uuid.DefaultGenerator.Generate(),
+			RequestTime:     t,
+		}
 
 		if r.URL.Query().Get(api.APIVersionKey) == admin.APIVersion || isAdminOp(r) {
 			correlationData.ClientPrincipalName = r.Header.Get("X-Ms-Client-Principal-Name")
@@ -83,7 +88,7 @@ func (l LogMiddleware) Log(h http.Handler) http.Handler {
 
 		ctx := r.Context()
 		ctx = context.WithValue(ctx, ContextKeyLog, log)
-		ctx = api.CtxWithCorrelationData(ctx, correlationData)
+		ctx = context.WithValue(ctx, ContextKeyCorrelationData, correlationData)
 
 		r = r.WithContext(ctx)
 
diff --git a/pkg/frontend/middleware/middleware.go b/pkg/frontend/middleware/middleware.go
index dd4f66f6e..f804aa3c5 100644
--- a/pkg/frontend/middleware/middleware.go
+++ b/pkg/frontend/middleware/middleware.go
@@ -9,5 +9,6 @@ const (
 	ContextKeyLog contextKey = iota
 	ContextKeyOriginalPath
 	ContextKeyBody
+	ContextKeyCorrelationData
 	ContextKeySystemData
 )
diff --git a/pkg/frontend/openshiftcluster_delete.go b/pkg/frontend/openshiftcluster_delete.go
index 9620b5286..40433fb3d 100644
--- a/pkg/frontend/openshiftcluster_delete.go
+++ b/pkg/frontend/openshiftcluster_delete.go
@@ -41,7 +41,7 @@ func (f *frontend) deleteOpenShiftCluster(w http.ResponseWriter, r *http.Request
 }
 
 func (f *frontend) _deleteOpenShiftCluster(ctx context.Context, r *http.Request, header *http.Header, doc *api.OpenShiftClusterDocument) error {
-	correlationData := api.GetCorrelationDataFromCtx(r.Context())
+	correlationData := r.Context().Value(middleware.ContextKeyCorrelationData).(*api.CorrelationData)
 
 	_, err := f.validateSubscriptionState(ctx, doc.Key, api.SubscriptionStateRegistered, api.SubscriptionStateWarned, api.SubscriptionStateSuspended)
 	if err != nil {
diff --git a/pkg/frontend/openshiftcluster_preflightvalidation.go b/pkg/frontend/openshiftcluster_preflightvalidation.go
index f9cea144b..3c102b6c4 100644
--- a/pkg/frontend/openshiftcluster_preflightvalidation.go
+++ b/pkg/frontend/openshiftcluster_preflightvalidation.go
@@ -6,7 +6,6 @@ package frontend
 import (
 	"context"
 	"encoding/json"
-	"fmt"
 	"net/http"
 	"strings"
 
@@ -77,18 +76,7 @@ func (f *frontend) preflightValidation(w http.ResponseWriter, r *http.Request) {
 
 func (f *frontend) _preflightValidation(ctx context.Context, log *logrus.Entry, raw json.RawMessage, apiVersion string, resourceID string) api.ValidationResult {
 	log.Infof("running preflight validation on resource: %s", resourceID)
-	dbOpenShiftClusters, err := f.dbGroup.OpenShiftClusters()
-	if err != nil {
-		log.Error(err)
-		return api.ValidationResult{
-			Status: api.ValidationStatusFailed,
-			Error: &api.CloudErrorBody{
-				Message: fmt.Sprintf("500: %s", api.CloudErrorCodeInternalServerError),
-			},
-		}
-	}
-
-	doc, err := dbOpenShiftClusters.Get(ctx, resourceID)
+	doc, err := f.dbOpenShiftClusters.Get(ctx, resourceID)
 	isCreate := cosmosdb.IsErrorStatusCode(err, http.StatusNotFound)
 	if err != nil && !isCreate {
 		log.Warning(err.Error())
diff --git a/pkg/frontend/openshiftcluster_putorpatch.go b/pkg/frontend/openshiftcluster_putorpatch.go
index a507f6419..cd2505765 100644
--- a/pkg/frontend/openshiftcluster_putorpatch.go
+++ b/pkg/frontend/openshiftcluster_putorpatch.go
@@ -53,7 +53,7 @@ func (f *frontend) putOrPatchOpenShiftCluster(w http.ResponseWriter, r *http.Req
 	var b []byte
 
 	body := r.Context().Value(middleware.ContextKeyBody).([]byte)
-	correlationData := api.GetCorrelationDataFromCtx(r.Context())
+	correlationData := r.Context().Value(middleware.ContextKeyCorrelationData).(*api.CorrelationData)
 	systemData, _ := r.Context().Value(middleware.ContextKeySystemData).(*api.SystemData) // don't panic
 	originalPath := r.Context().Value(middleware.ContextKeyOriginalPath).(string)
 	referer := r.Header.Get("Referer")
diff --git a/pkg/util/azureclient/applens/applens_client.go b/pkg/util/azureclient/applens/applens_client.go
index e44bc07e7..7a84fd3c1 100644
--- a/pkg/util/azureclient/applens/applens_client.go
+++ b/pkg/util/azureclient/applens/applens_client.go
@@ -7,6 +7,7 @@ package applens
 
 import (
 	"context"
+	"crypto/x509"
 	"encoding/json"
 	"fmt"
 	"io"
@@ -59,33 +60,19 @@ func NewClient(endpoint, issuerUrlTemplate, caName, scope string, cred azcore.To
 	return &Client{endpoint: endpoint, pipeline: *pipeline}, nil
 }
 
-func getClientOptions(initialOptions *ClientOptions, issuerUrlTemplate, caName string) (*ClientOptions, error) {
-	if initialOptions != nil {
-		return initialOptions, nil
-	}
-
-	if issuerUrlTemplate == "" || caName == "" {
-		return NewClientOptions(nil), nil
-	}
-
-	url := fmt.Sprintf(issuerUrlTemplate, caName)
-	rootCAs, err := pki.FetchDataFromGetIssuerPki(url)
-	if err != nil {
-		return nil, err
-	}
-
-	certPool, err := pki.BuildCertPoolForCaName(rootCAs)
-	if err != nil {
-		return nil, err
-	}
-
-	return NewClientOptions(certPool), nil
-}
-
-func newPipeline(authPolicy []policy.Policy, initialClientOptions *ClientOptions, issuerUrlTemplate, caName string) (*runtime.Pipeline, error) {
-	options, err := getClientOptions(initialClientOptions, issuerUrlTemplate, caName)
-	if err != nil {
-		return nil, err
+func newPipeline(authPolicy []policy.Policy, options *ClientOptions, issuerUrlTemplate, caName string) (*runtime.Pipeline, error) {
+	var cp *x509.CertPool = nil
+	var err error = nil
+	if options == nil {
+		// if provided pki info fetch the correct cert pool
+		// otherwise use the default of nil
+		if issuerUrlTemplate != "" && caName != "" {
+			cp, err = pki.GetTlsCertPool(issuerUrlTemplate, caName)
+			if err != nil {
+				return nil, err
+			}
+		}
+		options = NewClientOptions(cp)
 	}
 
 	runtimePipeline := runtime.NewPipeline(
diff --git a/pkg/util/azureclient/applens/applens_client_options.go b/pkg/util/azureclient/applens/applens_client_options.go
index ba77d4593..4387703c3 100644
--- a/pkg/util/azureclient/applens/applens_client_options.go
+++ b/pkg/util/azureclient/applens/applens_client_options.go
@@ -11,8 +11,6 @@ import (
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
-
-	"github.com/Azure/ARO-RP/pkg/util/azureclient"
 )
 
 const (
@@ -25,19 +23,6 @@ type ClientOptions struct {
 }
 
 func NewClientOptions(certPool *x509.CertPool) *ClientOptions {
-	httpTransport := &http.Transport{
-		TLSNextProto: make(map[string]func(authority string, c *tls.Conn) http.RoundTripper),
-		TLSClientConfig: &tls.Config{
-			RootCAs: certPool,
-		},
-	}
-
-	customRoundTripper := azureclient.NewCustomRoundTripper(httpTransport)
-
-	httpClient := &http.Client{
-		Transport: customRoundTripper,
-	}
-
 	return &ClientOptions{
 		azcore.ClientOptions{
 			Retry: policy.RetryOptions{
@@ -54,7 +39,14 @@ func NewClientOptions(certPool *x509.CertPool) *ClientOptions {
 				ApplicationID: userAgent,
 				Disabled:      false,
 			},
-			Transport: httpClient,
+			Transport: &http.Client{
+				Transport: &http.Transport{
+					TLSNextProto: make(map[string]func(authority string, c *tls.Conn) http.RoundTripper),
+					TLSClientConfig: &tls.Config{
+						RootCAs: certPool,
+					},
+				},
+			},
 		},
 	}
 }
diff --git a/pkg/util/azureclient/authz/remotepdp/client.go b/pkg/util/azureclient/authz/remotepdp/client.go
index 58a585140..8011bb63c 100644
--- a/pkg/util/azureclient/authz/remotepdp/client.go
+++ b/pkg/util/azureclient/authz/remotepdp/client.go
@@ -12,8 +12,6 @@ import (
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
-
-	"github.com/Azure/ARO-RP/pkg/util/azureclient"
 )
 
 // this asserts that &remotePDPClient{} would always implement RemotePDPClient
@@ -37,13 +35,6 @@ type remotePDPClient struct {
 func NewRemotePDPClient(endpoint, scope string, cred azcore.TokenCredential) *remotePDPClient {
 	authPolicy := runtime.NewBearerTokenPolicy(cred, []string{scope}, nil)
 
-	customRoundTripper := azureclient.NewCustomRoundTripper(http.DefaultTransport)
-	clientOptions := &azcore.ClientOptions{
-		Transport: &http.Client{
-			Transport: customRoundTripper,
-		},
-	}
-
 	pipeline := runtime.NewPipeline(
 		modulename,
 		version,
@@ -51,7 +42,7 @@ func NewRemotePDPClient(endpoint, scope string, cred azcore.TokenCredential) *re
 			PerCall:  []policy.Policy{},
 			PerRetry: []policy.Policy{authPolicy},
 		},
-		clientOptions,
+		nil,
 	)
 
 	return &remotePDPClient{endpoint, pipeline}
diff --git a/pkg/util/azureclient/authz/remotepdp/const.go b/pkg/util/azureclient/authz/remotepdp/const.go
index 73b4d32b6..1bbabc26d 100644
--- a/pkg/util/azureclient/authz/remotepdp/const.go
+++ b/pkg/util/azureclient/authz/remotepdp/const.go
@@ -17,4 +17,11 @@ const (
 	Allowed    AccessDecision = "Allowed"
 	NotAllowed AccessDecision = "NotAllowed"
 	Denied     AccessDecision = "Denied"
+
+	// GroupExpansion is the value to be used with ClaimName in SubjectAttributes
+	// This value gives CheckAccess a hint that it needs to retrieve all the groups the principal belongs to
+	// and then give the response based on all group entitlements.
+	//
+	// https://eng.ms/docs/microsoft-security/identity/auth/access-control-managed-identityacmi/azure-authz-data-plane/authz-dataplane-partner-wiki/remotepdp/checkaccess/samples/requestresponse
+	GroupExpansion = `{"groups":"src1"}`
 )
diff --git a/pkg/util/azureclient/authz/remotepdp/types.go b/pkg/util/azureclient/authz/remotepdp/types.go
index 2af8e97df..668287b4a 100644
--- a/pkg/util/azureclient/authz/remotepdp/types.go
+++ b/pkg/util/azureclient/authz/remotepdp/types.go
@@ -32,6 +32,7 @@ type SubjectAttributes struct {
 	AltSecId         string   `json:"altsecid,omitempty"`
 	IdentityProvider string   `json:"idp,omitempty"`
 	Issuer           string   `json:"iss,omitempty"`
+	ClaimName        string   `json:"_claim_names,omitempty"`
 }
 
 // ActionInfo contains an action the query checks whether the subject
diff --git a/pkg/util/azureclient/azuresdk/armstorage/accounts.go b/pkg/util/azureclient/azuresdk/armstorage/accounts.go
index e9e889677..e755d3076 100644
--- a/pkg/util/azureclient/azuresdk/armstorage/accounts.go
+++ b/pkg/util/azureclient/azuresdk/armstorage/accounts.go
@@ -5,7 +5,6 @@ package armstorage
 
 import (
 	"context"
-	"net/http"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
@@ -27,14 +26,9 @@ var _ AccountsClient = &accountsClient{}
 
 // NewAccountsClient creates a new AccountsClient
 func NewAccountsClient(environment *azureclient.AROEnvironment, subscriptionID string, credential azcore.TokenCredential) (AccountsClient, error) {
-	customRoundTripper := azureclient.NewCustomRoundTripper(http.DefaultTransport)
-
 	options := arm.ClientOptions{
 		ClientOptions: azcore.ClientOptions{
 			Cloud: environment.Cloud,
-			Transport: &http.Client{
-				Transport: customRoundTripper,
-			},
 		},
 	}
 	clientFactory, err := armstorage.NewClientFactory(subscriptionID, credential, &options)
diff --git a/pkg/util/azureclient/decorators.go b/pkg/util/azureclient/decorators.go
deleted file mode 100644
index f1977d9e8..000000000
--- a/pkg/util/azureclient/decorators.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package azureclient
-
-// Copyright (c) Microsoft Corporation.
-// Licensed under the Apache License 2.0.
-
-import (
-	"net/http"
-
-	"github.com/Azure/go-autorest/autorest"
-)
-
-// DecorateSenderWithLogging decorates a sender in order to intercept HTTP calls using a custom RoundTripper
-// and log low level HTTP request's information.
-func DecorateSenderWithLogging(sender autorest.Sender) autorest.Sender {
-	loggingDecorator := loggingDecorator()
-	return autorest.DecorateSender(sender, loggingDecorator, autorest.DoCloseIfError())
-}
-
-// loggingDecorator returns a function which is used to wrap and modify the behaviour of an autorest.Sender.
-// Azure Clients will have the sender wrapped by that function
-// in order to intercept http calls using our custom RoundTripper (through the adapter).
-func loggingDecorator() autorest.SendDecorator {
-	return func(s autorest.Sender) autorest.Sender {
-		rt := NewCustomRoundTripper(
-			&roundTripperAdapter{Sender: s},
-		)
-		return autorest.SenderFunc(rt.RoundTrip)
-	}
-}
-
-// roundTripperAdapter converts from autorest.Sender (internal field)
-// to http.RoundTripper (external method).
-type roundTripperAdapter struct {
-	Sender autorest.Sender
-}
-
-func (rta *roundTripperAdapter) RoundTrip(req *http.Request) (*http.Response, error) {
-	return rta.Sender.Do(req)
-}
diff --git a/pkg/util/azureclient/keyvault/base.go b/pkg/util/azureclient/keyvault/base.go
index c7ed31a9f..a867a959f 100644
--- a/pkg/util/azureclient/keyvault/base.go
+++ b/pkg/util/azureclient/keyvault/base.go
@@ -8,8 +8,6 @@ import (
 
 	azkeyvault "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.0/keyvault"
 	"github.com/Azure/go-autorest/autorest"
-
-	"github.com/Azure/ARO-RP/pkg/util/azureclient"
 )
 
 // BaseClient is a minimal interface for azure BaseClient
@@ -34,7 +32,6 @@ var _ BaseClient = &baseClient{}
 func New(authorizer autorest.Authorizer) BaseClient {
 	client := azkeyvault.New()
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &baseClient{
 		BaseClient: client,
diff --git a/pkg/util/azureclient/mgmt/authorization/denyassignment.go b/pkg/util/azureclient/mgmt/authorization/denyassignment.go
index 5c5f6908c..5e4b8e25f 100644
--- a/pkg/util/azureclient/mgmt/authorization/denyassignment.go
+++ b/pkg/util/azureclient/mgmt/authorization/denyassignment.go
@@ -25,7 +25,6 @@ var _ DenyAssignmentClient = &denyAssignmentClient{}
 func NewDenyAssignmentsClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) DenyAssignmentClient {
 	client := mgmtauthorization.NewDenyAssignmentsClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &denyAssignmentClient{
 		DenyAssignmentsClient: client,
diff --git a/pkg/util/azureclient/mgmt/authorization/roleassignments.go b/pkg/util/azureclient/mgmt/authorization/roleassignments.go
index c0cea6958..daa98cf2c 100644
--- a/pkg/util/azureclient/mgmt/authorization/roleassignments.go
+++ b/pkg/util/azureclient/mgmt/authorization/roleassignments.go
@@ -29,7 +29,6 @@ var _ RoleAssignmentsClient = &roleAssignmentsClient{}
 func NewRoleAssignmentsClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) RoleAssignmentsClient {
 	client := mgmtauthorization.NewRoleAssignmentsClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &roleAssignmentsClient{
 		RoleAssignmentsClient: client,
diff --git a/pkg/util/azureclient/mgmt/authorization/roledefinitions.go b/pkg/util/azureclient/mgmt/authorization/roledefinitions.go
index c10828361..29ff633cd 100644
--- a/pkg/util/azureclient/mgmt/authorization/roledefinitions.go
+++ b/pkg/util/azureclient/mgmt/authorization/roledefinitions.go
@@ -28,7 +28,6 @@ var _ RoleDefinitionsClient = &roleDefinitionsClient{}
 func NewRoleDefinitionsClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) RoleDefinitionsClient {
 	client := mgmtauthorization.NewRoleDefinitionsClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &roleDefinitionsClient{
 		RoleDefinitionsClient: client,
diff --git a/pkg/util/azureclient/mgmt/compute/diskencryptionsets.go b/pkg/util/azureclient/mgmt/compute/diskencryptionsets.go
index 6a3d1766d..f356836ae 100644
--- a/pkg/util/azureclient/mgmt/compute/diskencryptionsets.go
+++ b/pkg/util/azureclient/mgmt/compute/diskencryptionsets.go
@@ -27,7 +27,6 @@ var _ DiskEncryptionSetsClient = &diskEncryptionSetsClient{}
 func NewDiskEncryptionSetsClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) DiskEncryptionSetsClient {
 	client := mgmtcompute.NewDiskEncryptionSetsClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &diskEncryptionSetsClient{
 		DiskEncryptionSetsClient: client,
diff --git a/pkg/util/azureclient/mgmt/compute/disks.go b/pkg/util/azureclient/mgmt/compute/disks.go
index ff66cc663..4f550adb7 100644
--- a/pkg/util/azureclient/mgmt/compute/disks.go
+++ b/pkg/util/azureclient/mgmt/compute/disks.go
@@ -28,7 +28,6 @@ var _ DisksClient = &disksClient{}
 func NewDisksClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) DisksClient {
 	client := mgmtcompute.NewDisksClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &disksClient{
 		DisksClient: client,
diff --git a/pkg/util/azureclient/mgmt/compute/resourceskus.go b/pkg/util/azureclient/mgmt/compute/resourceskus.go
index 34152ab14..ac0006023 100644
--- a/pkg/util/azureclient/mgmt/compute/resourceskus.go
+++ b/pkg/util/azureclient/mgmt/compute/resourceskus.go
@@ -25,7 +25,6 @@ var _ ResourceSkusClient = &resourceSkusClient{}
 func NewResourceSkusClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) ResourceSkusClient {
 	client := mgmtcompute.NewResourceSkusClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &resourceSkusClient{
 		ResourceSkusClient: client,
diff --git a/pkg/util/azureclient/mgmt/compute/usage.go b/pkg/util/azureclient/mgmt/compute/usage.go
index 7a0586dc4..a3937d405 100644
--- a/pkg/util/azureclient/mgmt/compute/usage.go
+++ b/pkg/util/azureclient/mgmt/compute/usage.go
@@ -25,7 +25,6 @@ var _ UsageClient = &usageClient{}
 func NewUsageClient(environment *azureclient.AROEnvironment, tenantID string, authorizer autorest.Authorizer) UsageClient {
 	client := mgmtcompute.NewUsageClientWithBaseURI(environment.ResourceManagerEndpoint, tenantID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &usageClient{
 		UsageClient: client,
diff --git a/pkg/util/azureclient/mgmt/compute/virtualmachines.go b/pkg/util/azureclient/mgmt/compute/virtualmachines.go
index ef50ffddb..860d5d811 100644
--- a/pkg/util/azureclient/mgmt/compute/virtualmachines.go
+++ b/pkg/util/azureclient/mgmt/compute/virtualmachines.go
@@ -30,7 +30,6 @@ func NewVirtualMachinesClient(environment *azureclient.AROEnvironment, subscript
 	client := mgmtcompute.NewVirtualMachinesClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
 	client.PollingDuration = 30 * time.Minute
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &virtualMachinesClient{
 		VirtualMachinesClient: client,
diff --git a/pkg/util/azureclient/mgmt/compute/virtualmachinescalesetvms.go b/pkg/util/azureclient/mgmt/compute/virtualmachinescalesetvms.go
index a19e53598..3a59e35e2 100644
--- a/pkg/util/azureclient/mgmt/compute/virtualmachinescalesetvms.go
+++ b/pkg/util/azureclient/mgmt/compute/virtualmachinescalesetvms.go
@@ -30,7 +30,6 @@ func NewVirtualMachineScaleSetVMsClient(environment *azureclient.AROEnvironment,
 	client := mgmtcompute.NewVirtualMachineScaleSetVMsClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
 	client.PollingDuration = time.Hour
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &virtualMachineScaleSetVMsClient{
 		VirtualMachineScaleSetVMsClient: client,
diff --git a/pkg/util/azureclient/mgmt/compute/virtualmachinesscalesets.go b/pkg/util/azureclient/mgmt/compute/virtualmachinesscalesets.go
index 887afa465..31664dd15 100644
--- a/pkg/util/azureclient/mgmt/compute/virtualmachinesscalesets.go
+++ b/pkg/util/azureclient/mgmt/compute/virtualmachinesscalesets.go
@@ -24,7 +24,6 @@ var _ VirtualMachineScaleSetsClient = &virtualMachineScaleSetsClient{}
 func NewVirtualMachineScaleSetsClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) VirtualMachineScaleSetsClient {
 	client := mgmtcompute.NewVirtualMachineScaleSetsClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &virtualMachineScaleSetsClient{
 		VirtualMachineScaleSetsClient: client,
diff --git a/pkg/util/azureclient/mgmt/containerregistry/registries.go b/pkg/util/azureclient/mgmt/containerregistry/registries.go
index 78661e4c5..97ac29184 100644
--- a/pkg/util/azureclient/mgmt/containerregistry/registries.go
+++ b/pkg/util/azureclient/mgmt/containerregistry/registries.go
@@ -25,7 +25,6 @@ var _ RegistriesClient = &registriesClient{}
 func NewRegistriesClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) RegistriesClient {
 	client := mgmtcontainerregistry.NewRegistriesClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &registriesClient{
 		RegistriesClient: client,
diff --git a/pkg/util/azureclient/mgmt/containerregistry/tokens.go b/pkg/util/azureclient/mgmt/containerregistry/tokens.go
index d1c35fd58..f340adcd4 100644
--- a/pkg/util/azureclient/mgmt/containerregistry/tokens.go
+++ b/pkg/util/azureclient/mgmt/containerregistry/tokens.go
@@ -25,7 +25,6 @@ var _ TokensClient = &tokensClient{}
 func NewTokensClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) TokensClient {
 	client := mgmtcontainerregistry.NewTokensClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &tokensClient{
 		TokensClient: client,
diff --git a/pkg/util/azureclient/mgmt/containerservice/managedclusters.go b/pkg/util/azureclient/mgmt/containerservice/managedclusters.go
index b7bf9fbc7..279f2d65e 100644
--- a/pkg/util/azureclient/mgmt/containerservice/managedclusters.go
+++ b/pkg/util/azureclient/mgmt/containerservice/managedclusters.go
@@ -25,7 +25,6 @@ var _ ManagedClustersClient = &managedClustersClient{}
 func NewManagedClustersClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) ManagedClustersClient {
 	client := mgmtcontainerservice.NewManagedClustersClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &managedClustersClient{
 		ManagedClustersClient: client,
diff --git a/pkg/util/azureclient/mgmt/dns/recordsets.go b/pkg/util/azureclient/mgmt/dns/recordsets.go
index 6a26b2624..e688da2d2 100644
--- a/pkg/util/azureclient/mgmt/dns/recordsets.go
+++ b/pkg/util/azureclient/mgmt/dns/recordsets.go
@@ -29,7 +29,6 @@ var _ RecordSetsClient = &recordSetsClient{}
 func NewRecordSetsClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) RecordSetsClient {
 	client := mgmtdns.NewRecordSetsClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &recordSetsClient{
 		RecordSetsClient: client,
diff --git a/pkg/util/azureclient/mgmt/dns/zones.go b/pkg/util/azureclient/mgmt/dns/zones.go
index 4e8e8c5e4..babd9912d 100644
--- a/pkg/util/azureclient/mgmt/dns/zones.go
+++ b/pkg/util/azureclient/mgmt/dns/zones.go
@@ -27,7 +27,6 @@ var _ ZonesClient = &zonesClient{}
 func NewZonesClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) ZonesClient {
 	client := mgmtdns.NewZonesClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &zonesClient{
 		ZonesClient: client,
diff --git a/pkg/util/azureclient/mgmt/features/deployments.go b/pkg/util/azureclient/mgmt/features/deployments.go
index 656e93be5..128d21de0 100644
--- a/pkg/util/azureclient/mgmt/features/deployments.go
+++ b/pkg/util/azureclient/mgmt/features/deployments.go
@@ -31,7 +31,6 @@ func NewDeploymentsClient(environment *azureclient.AROEnvironment, subscriptionI
 	client.Authorizer = authorizer
 	client.PollingDelay = 10 * time.Second
 	client.PollingDuration = time.Hour
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &deploymentsClient{
 		DeploymentsClient: client,
diff --git a/pkg/util/azureclient/mgmt/features/providers.go b/pkg/util/azureclient/mgmt/features/providers.go
index eec19a165..2464e9e23 100644
--- a/pkg/util/azureclient/mgmt/features/providers.go
+++ b/pkg/util/azureclient/mgmt/features/providers.go
@@ -25,7 +25,6 @@ var _ ProvidersClient = &providersClient{}
 func NewProvidersClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) ProvidersClient {
 	client := mgmtfeatures.NewProvidersClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &providersClient{
 		ProvidersClient: client,
diff --git a/pkg/util/azureclient/mgmt/features/resourcegroups.go b/pkg/util/azureclient/mgmt/features/resourcegroups.go
index 969bf2491..a4cc1123d 100644
--- a/pkg/util/azureclient/mgmt/features/resourcegroups.go
+++ b/pkg/util/azureclient/mgmt/features/resourcegroups.go
@@ -33,7 +33,6 @@ func NewResourceGroupsClient(environment *azureclient.AROEnvironment, subscripti
 	client.Authorizer = authorizer
 	client.PollingDelay = 10 * time.Second
 	client.PollingDuration = time.Hour
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &resourceGroupsClient{
 		ResourceGroupsClient: client,
diff --git a/pkg/util/azureclient/mgmt/features/resources.go b/pkg/util/azureclient/mgmt/features/resources.go
index 1b8fc3a90..b3b3e6210 100644
--- a/pkg/util/azureclient/mgmt/features/resources.go
+++ b/pkg/util/azureclient/mgmt/features/resources.go
@@ -29,7 +29,6 @@ var _ ResourcesClient = &resourcesClient{}
 func NewResourcesClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) ResourcesClient {
 	client := mgmtfeatures.NewResourcesClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &resourcesClient{
 		ResourcesClient: client,
diff --git a/pkg/util/azureclient/mgmt/msi/userassignedidentities.go b/pkg/util/azureclient/mgmt/msi/userassignedidentities.go
index df75b4f3f..2c5eee797 100644
--- a/pkg/util/azureclient/mgmt/msi/userassignedidentities.go
+++ b/pkg/util/azureclient/mgmt/msi/userassignedidentities.go
@@ -27,7 +27,6 @@ var _ UserAssignedIdentitiesClient = &userAssignedIdentitiesClient{}
 func NewUserAssignedIdentitiesClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) UserAssignedIdentitiesClient {
 	client := mgmtmsi.NewUserAssignedIdentitiesClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &userAssignedIdentitiesClient{
 		UserAssignedIdentitiesClient: client,
diff --git a/pkg/util/azureclient/mgmt/network/flowlogs.go b/pkg/util/azureclient/mgmt/network/flowlogs.go
index 154310905..5e9420c06 100644
--- a/pkg/util/azureclient/mgmt/network/flowlogs.go
+++ b/pkg/util/azureclient/mgmt/network/flowlogs.go
@@ -29,8 +29,6 @@ var _ FlowLogsClient = &flowLogsClient{}
 func NewFlowLogsClient(environment *azureclient.AROEnvironment, tenantID string, authorizer autorest.Authorizer) FlowLogsClient {
 	client := mgmtnetwork.NewFlowLogsClientWithBaseURI(environment.ResourceManagerEndpoint, tenantID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
-
 	return &flowLogsClient{
 		FlowLogsClient: client,
 	}
diff --git a/pkg/util/azureclient/mgmt/network/interfaces.go b/pkg/util/azureclient/mgmt/network/interfaces.go
index bcbc48c4b..bece41965 100644
--- a/pkg/util/azureclient/mgmt/network/interfaces.go
+++ b/pkg/util/azureclient/mgmt/network/interfaces.go
@@ -28,7 +28,6 @@ var _ InterfacesClient = &interfacesClient{}
 func NewInterfacesClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) InterfacesClient {
 	client := mgmtnetwork.NewInterfacesClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &interfacesClient{
 		InterfacesClient: client,
diff --git a/pkg/util/azureclient/mgmt/network/loadbalancers.go b/pkg/util/azureclient/mgmt/network/loadbalancers.go
index 22fb7b22a..e276cb161 100644
--- a/pkg/util/azureclient/mgmt/network/loadbalancers.go
+++ b/pkg/util/azureclient/mgmt/network/loadbalancers.go
@@ -28,7 +28,6 @@ var _ LoadBalancersClient = &loadBalancersClient{}
 func NewLoadBalancersClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) LoadBalancersClient {
 	client := mgmtnetwork.NewLoadBalancersClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &loadBalancersClient{
 		LoadBalancersClient: client,
@@ -49,7 +48,6 @@ var _ LoadBalancerBackendAddressPoolsClient = &loadBalancerBackendAddressPoolsCl
 func NewLoadBalancerBackendAddressPoolsClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) LoadBalancerBackendAddressPoolsClient {
 	client := mgmtnetwork.NewLoadBalancerBackendAddressPoolsClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &loadBalancerBackendAddressPoolsClient{
 		LoadBalancerBackendAddressPoolsClient: client,
diff --git a/pkg/util/azureclient/mgmt/network/privateendpoints.go b/pkg/util/azureclient/mgmt/network/privateendpoints.go
index 48fb2ce90..d0f162237 100644
--- a/pkg/util/azureclient/mgmt/network/privateendpoints.go
+++ b/pkg/util/azureclient/mgmt/network/privateendpoints.go
@@ -28,7 +28,6 @@ var _ PrivateEndpointsClient = &privateEndpointsClient{}
 func NewPrivateEndpointsClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) PrivateEndpointsClient {
 	client := mgmtnetwork.NewPrivateEndpointsClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &privateEndpointsClient{
 		PrivateEndpointsClient: client,
diff --git a/pkg/util/azureclient/mgmt/network/privatelinkservices.go b/pkg/util/azureclient/mgmt/network/privatelinkservices.go
index b97389584..1aa99405e 100644
--- a/pkg/util/azureclient/mgmt/network/privatelinkservices.go
+++ b/pkg/util/azureclient/mgmt/network/privatelinkservices.go
@@ -30,7 +30,6 @@ var _ PrivateLinkServicesClient = &privateLinkServicesClient{}
 func NewPrivateLinkServicesClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) PrivateLinkServicesClient {
 	client := mgmtnetwork.NewPrivateLinkServicesClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &privateLinkServicesClient{
 		PrivateLinkServicesClient: client,
diff --git a/pkg/util/azureclient/mgmt/network/publicipaddresses.go b/pkg/util/azureclient/mgmt/network/publicipaddresses.go
index a820e2a6e..62202db9c 100644
--- a/pkg/util/azureclient/mgmt/network/publicipaddresses.go
+++ b/pkg/util/azureclient/mgmt/network/publicipaddresses.go
@@ -28,7 +28,6 @@ var _ PublicIPAddressesClient = &publicIPAddressesClient{}
 func NewPublicIPAddressesClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) PublicIPAddressesClient {
 	client := mgmtnetwork.NewPublicIPAddressesClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &publicIPAddressesClient{
 		PublicIPAddressesClient: client,
diff --git a/pkg/util/azureclient/mgmt/network/routetable.go b/pkg/util/azureclient/mgmt/network/routetable.go
index 623fed159..3d4185f0a 100644
--- a/pkg/util/azureclient/mgmt/network/routetable.go
+++ b/pkg/util/azureclient/mgmt/network/routetable.go
@@ -28,7 +28,6 @@ var _ RouteTablesClient = &routeTablesClient{}
 func NewRouteTablesClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) RouteTablesClient {
 	client := mgmtnetwork.NewRouteTablesClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &routeTablesClient{
 		RouteTablesClient: client,
diff --git a/pkg/util/azureclient/mgmt/network/securitygroups.go b/pkg/util/azureclient/mgmt/network/securitygroups.go
index fa3546dd7..c2d45a002 100644
--- a/pkg/util/azureclient/mgmt/network/securitygroups.go
+++ b/pkg/util/azureclient/mgmt/network/securitygroups.go
@@ -28,7 +28,6 @@ var _ SecurityGroupsClient = &securityGroupsClient{}
 func NewSecurityGroupsClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) SecurityGroupsClient {
 	client := mgmtnetwork.NewSecurityGroupsClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &securityGroupsClient{
 		SecurityGroupsClient: client,
diff --git a/pkg/util/azureclient/mgmt/network/subnets.go b/pkg/util/azureclient/mgmt/network/subnets.go
index 71319b0d0..02e3a0ccb 100644
--- a/pkg/util/azureclient/mgmt/network/subnets.go
+++ b/pkg/util/azureclient/mgmt/network/subnets.go
@@ -28,7 +28,6 @@ var _ SubnetsClient = &subnetsClient{}
 func NewSubnetsClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) SubnetsClient {
 	client := mgmtnetwork.NewSubnetsClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &subnetsClient{
 		SubnetsClient: client,
diff --git a/pkg/util/azureclient/mgmt/network/usage.go b/pkg/util/azureclient/mgmt/network/usage.go
index 72d42d2c1..da48de2c7 100644
--- a/pkg/util/azureclient/mgmt/network/usage.go
+++ b/pkg/util/azureclient/mgmt/network/usage.go
@@ -25,7 +25,6 @@ var _ UsageClient = &usageClient{}
 func NewUsageClient(environment *azureclient.AROEnvironment, tenantID string, authorizer autorest.Authorizer) UsageClient {
 	client := mgmtnetwork.NewUsagesClientWithBaseURI(environment.ResourceManagerEndpoint, tenantID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &usageClient{
 		UsagesClient: client,
diff --git a/pkg/util/azureclient/mgmt/network/virtualnetworkpeerings.go b/pkg/util/azureclient/mgmt/network/virtualnetworkpeerings.go
index 81b664c84..a7673d411 100644
--- a/pkg/util/azureclient/mgmt/network/virtualnetworkpeerings.go
+++ b/pkg/util/azureclient/mgmt/network/virtualnetworkpeerings.go
@@ -28,7 +28,6 @@ var _ VirtualNetworkPeeringsClient = &virtualNetworkPeeringsClient{}
 func NewVirtualNetworkPeeringsClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) VirtualNetworkPeeringsClient {
 	client := mgmtnetwork.NewVirtualNetworkPeeringsClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &virtualNetworkPeeringsClient{
 		VirtualNetworkPeeringsClient: client,
diff --git a/pkg/util/azureclient/mgmt/network/virtualnetworks.go b/pkg/util/azureclient/mgmt/network/virtualnetworks.go
index aed5a6d26..b90349ea2 100644
--- a/pkg/util/azureclient/mgmt/network/virtualnetworks.go
+++ b/pkg/util/azureclient/mgmt/network/virtualnetworks.go
@@ -27,7 +27,6 @@ var _ VirtualNetworksClient = &virtualNetworksClient{}
 func NewVirtualNetworksClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) VirtualNetworksClient {
 	client := mgmtnetwork.NewVirtualNetworksClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &virtualNetworksClient{
 		VirtualNetworksClient: client,
diff --git a/pkg/util/azureclient/mgmt/privatedns/privatezones.go b/pkg/util/azureclient/mgmt/privatedns/privatezones.go
index 427b42eb2..8fb2c402f 100644
--- a/pkg/util/azureclient/mgmt/privatedns/privatezones.go
+++ b/pkg/util/azureclient/mgmt/privatedns/privatezones.go
@@ -25,7 +25,6 @@ var _ PrivateZonesClient = &privateZonesClient{}
 func NewPrivateZonesClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) PrivateZonesClient {
 	client := mgmtprivatedns.NewPrivateZonesClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &privateZonesClient{
 		PrivateZonesClient: client,
diff --git a/pkg/util/azureclient/mgmt/privatedns/virtualnetworklinks.go b/pkg/util/azureclient/mgmt/privatedns/virtualnetworklinks.go
index 5ad4d679f..9872e8460 100644
--- a/pkg/util/azureclient/mgmt/privatedns/virtualnetworklinks.go
+++ b/pkg/util/azureclient/mgmt/privatedns/virtualnetworklinks.go
@@ -25,7 +25,6 @@ var _ VirtualNetworkLinksClient = &virtualNetworkLinksClient{}
 func NewVirtualNetworkLinksClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) VirtualNetworkLinksClient {
 	client := mgmtprivatedns.NewVirtualNetworkLinksClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
 
 	return &virtualNetworkLinksClient{
 		VirtualNetworkLinksClient: client,
diff --git a/pkg/util/azureclient/mgmt/storage/accounts.go b/pkg/util/azureclient/mgmt/storage/accounts.go
index b68fd0166..e19216796 100644
--- a/pkg/util/azureclient/mgmt/storage/accounts.go
+++ b/pkg/util/azureclient/mgmt/storage/accounts.go
@@ -30,8 +30,6 @@ var _ AccountsClient = &accountsClient{}
 func NewAccountsClient(environment *azureclient.AROEnvironment, subscriptionID string, authorizer autorest.Authorizer) AccountsClient {
 	client := mgmtstorage.NewAccountsClientWithBaseURI(environment.ResourceManagerEndpoint, subscriptionID)
 	client.Authorizer = authorizer
-	client.Sender = azureclient.DecorateSenderWithLogging(client.Sender)
-
 	return &accountsClient{
 		AccountsClient: client,
 	}
diff --git a/pkg/util/azureclient/roundtripper.go b/pkg/util/azureclient/roundtripper.go
deleted file mode 100644
index b49836bcf..000000000
--- a/pkg/util/azureclient/roundtripper.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package azureclient
-
-// Copyright (c) Microsoft Corporation.
-// Licensed under the Apache License 2.0.
-
-import (
-	"net/http"
-	"time"
-
-	"github.com/sirupsen/logrus"
-
-	"github.com/Azure/ARO-RP/pkg/api"
-	utillog "github.com/Azure/ARO-RP/pkg/util/log"
-)
-
-const (
-	// outboundRequests is the table name we configure in Geneva
-	// to send logs of outgoing requests from ARO-RP to ARM.
-	// https://docs.google.com/document/d/1RbnKKPNjw7kJZeR-2me4eu
-	outboundRequests = "outboundRequests"
-
-	responseCode         = "response_status_code"
-	contentLength        = "content_length"
-	durationMilliseconds = "duration_milliseconds"
-)
-
-func NewCustomRoundTripper(next http.RoundTripper) http.RoundTripper {
-	return &customRoundTripper{
-		next: next,
-	}
-}
-
-type customRoundTripper struct {
-	next http.RoundTripper
-}
-
-func (crt *customRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
-	correlationData := api.GetCorrelationDataFromCtx(req.Context())
-	if correlationData == nil {
-		correlationData = api.CreateCorrelationDataFromReq(req)
-	}
-
-	requestTime := time.Now()
-	l := updateCorrelationDataAndEnrichLogWithRequest(correlationData, utillog.GetLogger(), requestTime, req)
-
-	l.Info("HttpRequestStart")
-
-	res, err := crt.next.RoundTrip(req)
-
-	l = updateCorrelationDataAndEnrichLogWithResponse(correlationData, l, res, requestTime)
-	l.Info("HttpRequestEnd")
-
-	return res, err
-}
-
-// updateCorrelationDataAndEnrichLogWithRequest receives a non nil correlationData and updates the request time.
-// It also returns a new logrus entry updated with the new field for the LOGKIND entry (custom DGrep table for outgoing requests).
-func updateCorrelationDataAndEnrichLogWithRequest(correlationData *api.CorrelationData, l *logrus.Entry, requestTime time.Time, req *http.Request) *logrus.Entry {
-	correlationData.RequestTime = requestTime
-
-	l = utillog.EnrichWithCorrelationData(l, correlationData)
-	l = l.WithFields(logrus.Fields{
-		"request_URL": req.URL,
-		"LOGKIND":     outboundRequests,
-	})
-
-	return l
-}
-
-func updateCorrelationDataAndEnrichLogWithResponse(correlationData *api.CorrelationData, l *logrus.Entry, res *http.Response, requestTime time.Time) *logrus.Entry {
-	if res == nil {
-		return l.WithFields(logrus.Fields{
-			responseCode:         "0",
-			durationMilliseconds: time.Since(requestTime).Milliseconds(),
-		})
-	}
-
-	correlationData.CorrelationID = res.Header.Get("X-Ms-Correlation-Request-Id")
-	l = utillog.EnrichWithCorrelationData(l, correlationData)
-
-	return l.WithFields(logrus.Fields{
-		responseCode:         res.StatusCode,
-		contentLength:        res.ContentLength,
-		durationMilliseconds: time.Since(requestTime).Milliseconds(),
-	})
-}
diff --git a/pkg/util/cluster/cluster.go b/pkg/util/cluster/cluster.go
index 9860c9d57..293cfa122 100644
--- a/pkg/util/cluster/cluster.go
+++ b/pkg/util/cluster/cluster.go
@@ -42,7 +42,6 @@ import (
 	"github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/features"
 	"github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/network"
 	redhatopenshift20231122 "github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/redhatopenshift/2023-11-22/redhatopenshift"
-	"github.com/Azure/ARO-RP/pkg/util/azureerrors"
 	utilgraph "github.com/Azure/ARO-RP/pkg/util/graph"
 	"github.com/Azure/ARO-RP/pkg/util/rbac"
 	"github.com/Azure/ARO-RP/pkg/util/rolesets"
@@ -134,26 +133,31 @@ func New(log *logrus.Entry, environment env.Core, ci bool) (*Cluster, error) {
 	return c, nil
 }
 
-type appDetails struct {
-	applicationId     string
-	applicationSecret string
-	SPId              string
-}
-
-func (c *Cluster) createApp(ctx context.Context, clusterName string) (applicationDetails appDetails, err error) {
-	c.log.Infof("Creating AAD application")
+func (c *Cluster) CreateApp(ctx context.Context, clusterName string) error {
+	c.log.Infof("creating AAD application")
 	appID, appSecret, err := c.createApplication(ctx, "aro-"+clusterName)
 	if err != nil {
-		return appDetails{}, err
+		return err
 	}
 
-	c.log.Infof("Creating service principal")
+	c.log.Infof("creating service principal")
 	spID, err := c.createServicePrincipal(ctx, appID)
 	if err != nil {
-		return appDetails{}, err
+		return err
 	}
 
-	return appDetails{appID, appSecret, spID}, nil
+	return os.WriteFile("clusterapp.env", []byte(fmt.Sprintf("export AZURE_CLUSTER_SERVICE_PRINCIPAL_ID=%s\nexport AZURE_CLUSTER_APP_ID=%s\nexport AZURE_CLUSTER_APP_SECRET=%s", spID, appID, appSecret)), 0o600)
+}
+
+func (c *Cluster) DeleteApp(ctx context.Context) error {
+	err := env.ValidateVars(
+		"AZURE_CLUSTER_APP_ID",
+	)
+	if err != nil {
+		return err
+	}
+
+	return c.deleteApplication(ctx, os.Getenv("AZURE_CLUSTER_APP_ID"))
 }
 
 func (c *Cluster) Create(ctx context.Context, vnetResourceGroup, clusterName string, osClusterVersion string) error {
@@ -166,16 +170,21 @@ func (c *Cluster) Create(ctx context.Context, vnetResourceGroup, clusterName str
 		return nil
 	}
 
-	fpSPId := os.Getenv("AZURE_FP_SERVICE_PRINCIPAL_ID")
-	if fpSPId == "" {
-		return fmt.Errorf("fp service principal id is not found")
-	}
-
-	appDetails, err := c.createApp(ctx, clusterName)
+	err = env.ValidateVars(
+		"AZURE_FP_SERVICE_PRINCIPAL_ID",
+		"AZURE_CLUSTER_SERVICE_PRINCIPAL_ID",
+		"AZURE_CLUSTER_APP_ID",
+		"AZURE_CLUSTER_APP_SECRET",
+	)
 	if err != nil {
 		return err
 	}
 
+	fpSPID := os.Getenv("AZURE_FP_SERVICE_PRINCIPAL_ID")
+	spID := os.Getenv("AZURE_CLUSTER_SERVICE_PRINCIPAL_ID")
+	appID := os.Getenv("AZURE_CLUSTER_APP_ID")
+	appSecret := os.Getenv("AZURE_CLUSTER_APP_SECRET")
+
 	visibility := api.VisibilityPublic
 
 	if os.Getenv("PRIVATE_CLUSTER") != "" || os.Getenv("NO_INTERNET") != "" {
@@ -231,8 +240,8 @@ func (c *Cluster) Create(ctx context.Context, vnetResourceGroup, clusterName str
 	parameters := map[string]*arm.ParametersParameter{
 		"clusterName":               {Value: clusterName},
 		"ci":                        {Value: c.ci},
-		"clusterServicePrincipalId": {Value: appDetails.SPId},
-		"fpServicePrincipalId":      {Value: fpSPId},
+		"clusterServicePrincipalId": {Value: spID},
+		"fpServicePrincipalId":      {Value: fpSPID},
 		"vnetAddressPrefix":         {Value: addressPrefix},
 		"masterAddressPrefix":       {Value: masterSubnet},
 		"workerAddressPrefix":       {Value: workerSubnet},
@@ -283,7 +292,7 @@ func (c *Cluster) Create(ctx context.Context, vnetResourceGroup, clusterName str
 		{"/subscriptions/" + c.env.SubscriptionID() + "/resourceGroups/" + vnetResourceGroup + "/providers/Microsoft.Network/routeTables/" + clusterName + "-rt", rbac.RoleNetworkContributor},
 		{diskEncryptionSetID, rbac.RoleReader},
 	} {
-		for _, principalID := range []string{appDetails.SPId, fpSPId} {
+		for _, principalID := range []string{spID, fpSPID} {
 			for i := 0; i < 5; i++ {
 				_, err = c.roleassignments.Create(
 					ctx,
@@ -322,7 +331,7 @@ func (c *Cluster) Create(ctx context.Context, vnetResourceGroup, clusterName str
 	}
 
 	c.log.Info("creating cluster")
-	err = c.createCluster(ctx, vnetResourceGroup, clusterName, appDetails.applicationId, appDetails.applicationSecret, diskEncryptionSetID, visibility, osClusterVersion)
+	err = c.createCluster(ctx, vnetResourceGroup, clusterName, appID, appSecret, diskEncryptionSetID, visibility, osClusterVersion)
 
 	if err != nil {
 		return err
@@ -367,29 +376,18 @@ func (c *Cluster) generateSubnets() (vnetPrefix string, masterSubnet string, wor
 }
 
 func (c *Cluster) Delete(ctx context.Context, vnetResourceGroup, clusterName string) error {
-	c.log.Infof("Deleting cluster %s in resource group %s", clusterName, vnetResourceGroup)
 	var errs []error
 
-	if c.ci {
-		oc, err := c.openshiftclusters.Get(ctx, vnetResourceGroup, clusterName)
-		clusterResourceGroup := fmt.Sprintf("aro-%s", clusterName)
-		if err != nil {
-			c.log.Errorf("CI E2E cluster %s not found in resource group %s", clusterName, vnetResourceGroup)
-			errs = append(errs, err)
-		}
+	switch {
+	case c.ci && env.IsLocalDevelopmentMode(): // PR E2E
 		errs = append(errs,
-			c.deleteApplication(ctx, *oc.OpenShiftClusterProperties.ServicePrincipalProfile.ClientID),
 			c.deleteCluster(ctx, vnetResourceGroup, clusterName),
-			c.ensureResourceGroupDeleted(ctx, clusterResourceGroup),
-			c.deleteResourceGroup(ctx, vnetResourceGroup),
+			c.deleteClusterResourceGroup(ctx, vnetResourceGroup),
+			c.deleteVnetPeerings(ctx, vnetResourceGroup),
 		)
-
-		if env.IsLocalDevelopmentMode() { //PR E2E
-			errs = append(errs,
-				c.deleteVnetPeerings(ctx, vnetResourceGroup),
-			)
-		}
-	} else {
+	case c.ci: // Prod E2E
+		errs = append(errs, c.deleteClusterResourceGroup(ctx, vnetResourceGroup))
+	default:
 		errs = append(errs,
 			c.deleteRoleAssignments(ctx, vnetResourceGroup, clusterName),
 			c.deleteCluster(ctx, vnetResourceGroup, clusterName),
@@ -716,28 +714,13 @@ func (c *Cluster) deleteCluster(ctx context.Context, resourceGroup, clusterName
 	return nil
 }
 
-func (c *Cluster) ensureResourceGroupDeleted(ctx context.Context, resourceGroupName string) error {
-	c.log.Printf("deleting resource group %s", resourceGroupName)
-	timeoutCtx, cancel := context.WithTimeout(ctx, 10*time.Minute)
-	defer cancel()
-
-	return wait.PollImmediateUntil(5*time.Second, func() (bool, error) {
-		_, err := c.groups.Get(ctx, resourceGroupName)
-		if azureerrors.ResourceGroupNotFound(err) {
-			c.log.Infof("finished deleting resource group %s", resourceGroupName)
-			return true, nil
-		}
-		return false, fmt.Errorf("failed to delete resource group %s with %s", resourceGroupName, err)
-	}, timeoutCtx.Done())
-}
-
-func (c *Cluster) deleteResourceGroup(ctx context.Context, resourceGroup string) error {
-	c.log.Printf("deleting resource group %s", resourceGroup)
+func (c *Cluster) deleteClusterResourceGroup(ctx context.Context, resourceGroup string) error {
 	if _, err := c.groups.Get(ctx, resourceGroup); err != nil {
 		c.log.Printf("error getting resource group %s, skipping deletion: %v", resourceGroup, err)
 		return nil
 	}
 
+	c.log.Printf("deleting resource group %s", resourceGroup)
 	if err := c.groups.DeleteAndWait(ctx, resourceGroup); err != nil {
 		return fmt.Errorf("error deleting resource group: %w", err)
 	}
diff --git a/pkg/util/log/log.go b/pkg/util/log/log.go
index 76300b4ff..82a890d38 100644
--- a/pkg/util/log/log.go
+++ b/pkg/util/log/log.go
@@ -143,10 +143,6 @@ func EnrichWithPath(log *logrus.Entry, path string) *logrus.Entry {
 // EnrichWithCorrelationData sets log fields based on an optional
 // correlationData struct
 func EnrichWithCorrelationData(log *logrus.Entry, correlationData *api.CorrelationData) *logrus.Entry {
-	if log == nil {
-		log = GetLogger()
-	}
-
 	if correlationData == nil {
 		return log
 	}
@@ -154,10 +150,8 @@ func EnrichWithCorrelationData(log *logrus.Entry, correlationData *api.Correlati
 	return log.WithFields(logrus.Fields{
 		"correlation_id":        correlationData.CorrelationID,
 		"client_request_id":     correlationData.ClientRequestID,
-		"operation_id":          correlationData.OperationID,
 		"request_id":            correlationData.RequestID,
 		"client_principal_name": correlationData.ClientPrincipalName,
-		"request_time":          correlationData.RequestTime,
 	})
 }
 
diff --git a/pkg/util/mocks/net/net.go b/pkg/util/mocks/net/net.go
deleted file mode 100644
index 7031e1736..000000000
--- a/pkg/util/mocks/net/net.go
+++ /dev/null
@@ -1,208 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: github.com/Azure/ARO-RP/pkg/util/net (interfaces: DNSIClient)
-
-// Package mock_net is a generated GoMock package.
-package mock_net
-
-import (
-	context "context"
-	reflect "reflect"
-
-	gomock "github.com/golang/mock/gomock"
-	v1 "github.com/openshift/api/config/v1"
-	v10 "github.com/openshift/client-go/config/applyconfigurations/config/v1"
-	v11 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	types "k8s.io/apimachinery/pkg/types"
-	watch "k8s.io/apimachinery/pkg/watch"
-)
-
-// MockDNSIClient is a mock of DNSIClient interface.
-type MockDNSIClient struct {
-	ctrl     *gomock.Controller
-	recorder *MockDNSIClientMockRecorder
-}
-
-// MockDNSIClientMockRecorder is the mock recorder for MockDNSIClient.
-type MockDNSIClientMockRecorder struct {
-	mock *MockDNSIClient
-}
-
-// NewMockDNSIClient creates a new mock instance.
-func NewMockDNSIClient(ctrl *gomock.Controller) *MockDNSIClient {
-	mock := &MockDNSIClient{ctrl: ctrl}
-	mock.recorder = &MockDNSIClientMockRecorder{mock}
-	return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockDNSIClient) EXPECT() *MockDNSIClientMockRecorder {
-	return m.recorder
-}
-
-// Apply mocks base method.
-func (m *MockDNSIClient) Apply(arg0 context.Context, arg1 *v10.DNSApplyConfiguration, arg2 v11.ApplyOptions) (*v1.DNS, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Apply", arg0, arg1, arg2)
-	ret0, _ := ret[0].(*v1.DNS)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// Apply indicates an expected call of Apply.
-func (mr *MockDNSIClientMockRecorder) Apply(arg0, arg1, arg2 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Apply", reflect.TypeOf((*MockDNSIClient)(nil).Apply), arg0, arg1, arg2)
-}
-
-// ApplyStatus mocks base method.
-func (m *MockDNSIClient) ApplyStatus(arg0 context.Context, arg1 *v10.DNSApplyConfiguration, arg2 v11.ApplyOptions) (*v1.DNS, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "ApplyStatus", arg0, arg1, arg2)
-	ret0, _ := ret[0].(*v1.DNS)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// ApplyStatus indicates an expected call of ApplyStatus.
-func (mr *MockDNSIClientMockRecorder) ApplyStatus(arg0, arg1, arg2 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyStatus", reflect.TypeOf((*MockDNSIClient)(nil).ApplyStatus), arg0, arg1, arg2)
-}
-
-// Create mocks base method.
-func (m *MockDNSIClient) Create(arg0 context.Context, arg1 *v1.DNS, arg2 v11.CreateOptions) (*v1.DNS, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Create", arg0, arg1, arg2)
-	ret0, _ := ret[0].(*v1.DNS)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// Create indicates an expected call of Create.
-func (mr *MockDNSIClientMockRecorder) Create(arg0, arg1, arg2 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockDNSIClient)(nil).Create), arg0, arg1, arg2)
-}
-
-// Delete mocks base method.
-func (m *MockDNSIClient) Delete(arg0 context.Context, arg1 string, arg2 v11.DeleteOptions) error {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Delete", arg0, arg1, arg2)
-	ret0, _ := ret[0].(error)
-	return ret0
-}
-
-// Delete indicates an expected call of Delete.
-func (mr *MockDNSIClientMockRecorder) Delete(arg0, arg1, arg2 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockDNSIClient)(nil).Delete), arg0, arg1, arg2)
-}
-
-// DeleteCollection mocks base method.
-func (m *MockDNSIClient) DeleteCollection(arg0 context.Context, arg1 v11.DeleteOptions, arg2 v11.ListOptions) error {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "DeleteCollection", arg0, arg1, arg2)
-	ret0, _ := ret[0].(error)
-	return ret0
-}
-
-// DeleteCollection indicates an expected call of DeleteCollection.
-func (mr *MockDNSIClientMockRecorder) DeleteCollection(arg0, arg1, arg2 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCollection", reflect.TypeOf((*MockDNSIClient)(nil).DeleteCollection), arg0, arg1, arg2)
-}
-
-// Get mocks base method.
-func (m *MockDNSIClient) Get(arg0 context.Context, arg1 string, arg2 v11.GetOptions) (*v1.DNS, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Get", arg0, arg1, arg2)
-	ret0, _ := ret[0].(*v1.DNS)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// Get indicates an expected call of Get.
-func (mr *MockDNSIClientMockRecorder) Get(arg0, arg1, arg2 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockDNSIClient)(nil).Get), arg0, arg1, arg2)
-}
-
-// List mocks base method.
-func (m *MockDNSIClient) List(arg0 context.Context, arg1 v11.ListOptions) (*v1.DNSList, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "List", arg0, arg1)
-	ret0, _ := ret[0].(*v1.DNSList)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// List indicates an expected call of List.
-func (mr *MockDNSIClientMockRecorder) List(arg0, arg1 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockDNSIClient)(nil).List), arg0, arg1)
-}
-
-// Patch mocks base method.
-func (m *MockDNSIClient) Patch(arg0 context.Context, arg1 string, arg2 types.PatchType, arg3 []byte, arg4 v11.PatchOptions, arg5 ...string) (*v1.DNS, error) {
-	m.ctrl.T.Helper()
-	varargs := []interface{}{arg0, arg1, arg2, arg3, arg4}
-	for _, a := range arg5 {
-		varargs = append(varargs, a)
-	}
-	ret := m.ctrl.Call(m, "Patch", varargs...)
-	ret0, _ := ret[0].(*v1.DNS)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// Patch indicates an expected call of Patch.
-func (mr *MockDNSIClientMockRecorder) Patch(arg0, arg1, arg2, arg3, arg4 interface{}, arg5 ...interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	varargs := append([]interface{}{arg0, arg1, arg2, arg3, arg4}, arg5...)
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockDNSIClient)(nil).Patch), varargs...)
-}
-
-// Update mocks base method.
-func (m *MockDNSIClient) Update(arg0 context.Context, arg1 *v1.DNS, arg2 v11.UpdateOptions) (*v1.DNS, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Update", arg0, arg1, arg2)
-	ret0, _ := ret[0].(*v1.DNS)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// Update indicates an expected call of Update.
-func (mr *MockDNSIClientMockRecorder) Update(arg0, arg1, arg2 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockDNSIClient)(nil).Update), arg0, arg1, arg2)
-}
-
-// UpdateStatus mocks base method.
-func (m *MockDNSIClient) UpdateStatus(arg0 context.Context, arg1 *v1.DNS, arg2 v11.UpdateOptions) (*v1.DNS, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "UpdateStatus", arg0, arg1, arg2)
-	ret0, _ := ret[0].(*v1.DNS)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// UpdateStatus indicates an expected call of UpdateStatus.
-func (mr *MockDNSIClientMockRecorder) UpdateStatus(arg0, arg1, arg2 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStatus", reflect.TypeOf((*MockDNSIClient)(nil).UpdateStatus), arg0, arg1, arg2)
-}
-
-// Watch mocks base method.
-func (m *MockDNSIClient) Watch(arg0 context.Context, arg1 v11.ListOptions) (watch.Interface, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Watch", arg0, arg1)
-	ret0, _ := ret[0].(watch.Interface)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// Watch indicates an expected call of Watch.
-func (mr *MockDNSIClientMockRecorder) Watch(arg0, arg1 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockDNSIClient)(nil).Watch), arg0, arg1)
-}
diff --git a/pkg/util/net/generate.go b/pkg/util/net/generate.go
deleted file mode 100644
index 68d62f456..000000000
--- a/pkg/util/net/generate.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package net
-
-// Copyright (c) Microsoft Corporation.
-// Licensed under the Apache License 2.0.
-
-//go:generate rm -rf ../../util/mocks/$GOPACKAGE
-//go:generate go run ../../../vendor/github.com/golang/mock/mockgen -destination=../../../pkg/util/mocks/$GOPACKAGE/$GOPACKAGE.go github.com/Azure/ARO-RP/pkg/util/$GOPACKAGE DNSIClient
-//go:generate go run ../../../vendor/golang.org/x/tools/cmd/goimports -local=github.com/Azure/ARO-RP -e -w ../../../pkg/util/mocks/$GOPACKAGE/$GOPACKAGE.go
diff --git a/pkg/util/net/net.go b/pkg/util/net/net.go
index 4512d3fd1..5a39bdbbe 100644
--- a/pkg/util/net/net.go
+++ b/pkg/util/net/net.go
@@ -4,26 +4,9 @@ package net
 // Licensed under the Apache License 2.0.
 
 import (
-	"context"
 	"errors"
 	"net"
-	"strings"
 	"syscall"
-
-	mgmtprivatedns "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns"
-	"github.com/Azure/go-autorest/autorest/azure"
-	configclient "github.com/openshift/client-go/config/clientset/versioned"
-	v1 "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
-	mcoclient "github.com/openshift/machine-config-operator/pkg/generated/clientset/versioned"
-	"github.com/sirupsen/logrus"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/client-go/kubernetes"
-	"k8s.io/client-go/util/retry"
-
-	"github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/privatedns"
-	"github.com/Azure/ARO-RP/pkg/util/ready"
-	"github.com/Azure/ARO-RP/pkg/util/stringutils"
-	"github.com/Azure/ARO-RP/pkg/util/version"
 )
 
 // Listen returns a listener with its send and receive buffer sizes set, such
@@ -89,165 +72,3 @@ func setBuffers(rc syscall.RawConn, sz int) error {
 
 	return err
 }
-
-type PrivateZoneRemovalConfig struct {
-	Log                *logrus.Entry
-	PrivateZonesClient privatedns.PrivateZonesClient
-	Configcli          configclient.Interface
-	Mcocli             mcoclient.Interface
-	Kubernetescli      kubernetes.Interface
-	VNetLinksClient    privatedns.VirtualNetworkLinksClient
-	ResourceGroupID    string
-}
-
-func RemovePrivateDNSZone(ctx context.Context, config PrivateZoneRemovalConfig) error {
-	if config.PrivateZonesClient == nil {
-		return errors.New("privateZonesClient is nil")
-	}
-
-	resourceGroup := stringutils.LastTokenByte(config.ResourceGroupID, '/')
-	privateZones, err := config.PrivateZonesClient.ListByResourceGroup(ctx, resourceGroup, nil)
-	if err != nil {
-		config.Log.Print(err)
-		return nil
-	}
-
-	if config.Configcli == nil {
-		return errors.New("configcli is nil")
-	}
-
-	if len(privateZones) == 0 {
-		// fix up any clusters that we already upgraded
-		if err := UpdateDNSes(ctx, config.Configcli.ConfigV1().DNSes(), config.ResourceGroupID); err != nil {
-			config.Log.Print(err)
-		}
-		return nil
-	}
-
-	if config.Mcocli == nil {
-		return errors.New("mcocli is nil")
-	}
-
-	if config.Kubernetescli == nil {
-		return errors.New("kubernetescli is nil")
-	}
-
-	mcpLister := config.Mcocli.MachineconfigurationV1().MachineConfigPools()
-	nodeLister := config.Kubernetescli.CoreV1().Nodes()
-	sameNumberOfNodesAndMachines, err := ready.SameNumberOfNodesAndMachines(ctx, mcpLister, nodeLister)
-	if err != nil {
-		config.Log.Print(err)
-		return nil
-	}
-
-	if !sameNumberOfNodesAndMachines {
-		return nil
-	}
-
-	clusterVersionIsLessThan4_4, err := version.ClusterVersionIsLessThan4_4(ctx, config.Configcli)
-	if err != nil {
-		config.Log.Print(err)
-		return nil
-	}
-
-	if clusterVersionIsLessThan4_4 {
-		config.Log.Printf("cluster version < 4.4, not removing private DNS zone")
-		return nil
-	}
-
-	if err = UpdateDNSes(ctx, config.Configcli.ConfigV1().DNSes(), config.ResourceGroupID); err != nil {
-		config.Log.Print(err)
-		return nil
-	}
-
-	if err := RemoveZones(ctx, config.VNetLinksClient, config.PrivateZonesClient, privateZones, resourceGroup); err != nil {
-		config.Log.Print(err)
-		return nil
-	}
-	return nil
-}
-
-func DeletePrivateDNSVNetLinks(ctx context.Context, vNetLinksClient privatedns.VirtualNetworkLinksClient, resourceID string) error {
-	r, err := azure.ParseResourceID(resourceID)
-	if err != nil {
-		return err
-	}
-
-	if vNetLinksClient == nil {
-		return errors.New("vNetLinksClient is nil")
-	}
-
-	vNetLinks, err := vNetLinksClient.List(ctx, r.ResourceGroup, r.ResourceName, nil)
-	if err != nil {
-		return err
-	}
-
-	for _, vNetLink := range vNetLinks {
-		err = vNetLinksClient.DeleteAndWait(ctx, r.ResourceGroup, r.ResourceName, *vNetLink.Name, "")
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-func RemoveZones(ctx context.Context,
-	vNetLinksClient privatedns.VirtualNetworkLinksClient,
-	privateZoneClient privatedns.PrivateZonesClient,
-	privateZones []mgmtprivatedns.PrivateZone,
-	resourceGroup string) error {
-	for _, privateZone := range privateZones {
-		if err := DeletePrivateDNSVNetLinks(ctx, vNetLinksClient, *privateZone.ID); err != nil {
-			return err
-		}
-
-		r, err := azure.ParseResourceID(*privateZone.ID)
-		if err != nil {
-			return err
-		}
-
-		if privateZoneClient == nil {
-			return errors.New("privateZoneClient is nil")
-		}
-
-		if err := privateZoneClient.DeleteAndWait(ctx, resourceGroup, r.ResourceName, ""); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func UpdateDNSes(ctx context.Context, dnsI DNSIClient, resourceGroupID string) error {
-	fn := updateClusterDNSFn(ctx, dnsI, resourceGroupID)
-	return retry.RetryOnConflict(retry.DefaultRetry, fn)
-}
-
-type DNSIClient interface {
-	v1.DNSInterface
-}
-
-func updateClusterDNSFn(ctx context.Context, dnsClient DNSIClient, resourceGroupID string) func() error {
-	return func() error {
-		if dnsClient == nil {
-			return errors.New("dnsClient interface is nil")
-		}
-
-		dns, err := dnsClient.Get(ctx, "cluster", metav1.GetOptions{})
-		if err != nil {
-			return err
-		}
-
-		if dns.Spec.PrivateZone == nil ||
-			!strings.HasPrefix(
-				strings.ToLower(dns.Spec.PrivateZone.ID),
-				strings.ToLower(resourceGroupID)) {
-			return nil
-		}
-
-		dns.Spec.PrivateZone = nil
-
-		_, err = dnsClient.Update(ctx, dns, metav1.UpdateOptions{})
-		return err
-	}
-}
diff --git a/pkg/util/pki/fetchdata.go b/pkg/util/pki/fetchdata.go
deleted file mode 100644
index 08fc7a136..000000000
--- a/pkg/util/pki/fetchdata.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package pki
-
-// Copyright (c) Microsoft Corporation.
-// Licensed under the Apache License 2.0.
-
-import (
-	"encoding/json"
-	"io"
-	"net/http"
-)
-
-// https://aka.ms/getissuers
-// The v3 endpoint can be used to get ca certs
-// For example https://issuer.pki.azure.com/dsms/issuercertificates?getissuersv3&caName=ame
-// returns the ame certs
-func FetchDataFromGetIssuerPki(url string) (*RootCAs, error) {
-	response, err := http.Get(url)
-	if err != nil {
-		return nil, err
-	}
-
-	defer response.Body.Close()
-
-	body, err := io.ReadAll(response.Body)
-	if err != nil {
-		return nil, err
-	}
-
-	var rootCAs RootCAs
-	json.Unmarshal(body, &rootCAs)
-	return &rootCAs, nil
-}
diff --git a/pkg/util/pki/pki.go b/pkg/util/pki/pki.go
index c845169a8..66e02dcbb 100644
--- a/pkg/util/pki/pki.go
+++ b/pkg/util/pki/pki.go
@@ -5,9 +5,17 @@ package pki
 
 import (
 	"crypto/x509"
+	"encoding/json"
 	"errors"
+	"fmt"
+	"io"
+	"net/http"
+	"sync"
 )
 
+var caMap map[string]x509.CertPool = make(map[string]x509.CertPool)
+var mu sync.RWMutex
+
 type RootCAs struct {
 	RootsInfos []RootInfo `json:"RootsInfos"`
 }
@@ -34,8 +42,76 @@ type IntermediateInfo struct {
 	PEM              string `json:"PEM"`
 }
 
-func BuildCertPoolForCaName(data *RootCAs) (*x509.CertPool, error) {
+// https://aka.ms/getissuers
+// The v3 endpoint can be used to get ca certs
+// For example https://issuer.pki.azure.com/dsms/issuercertificates?getissuersv3&caName=ame
+// returns the ame certs
+func FetchDataFromGetIssuerPki(url string) (*RootCAs, error) {
+	response, err := http.Get(url)
+
+	if err != nil {
+		return nil, err
+	}
+
+	defer response.Body.Close()
+
+	if err != nil {
+		return nil, err
+	}
+
+	// Read in certs from endpoint
+	body, err := io.ReadAll(response.Body)
+
+	if err != nil {
+		return nil, err
+	}
+
+	var rootCAs RootCAs
+	json.Unmarshal(body, &rootCAs)
+	return &rootCAs, nil
+}
+
+func GetTlsCertPool(urlTemplate, caName string) (*x509.CertPool, error) {
+	url := fmt.Sprintf(urlTemplate, caName)
+	caCertPool, ok := getCaCertPoolFromMap(url)
+	if ok {
+		return &caCertPool, nil
+	} else {
+		caCertPool, err := buildCertPoolForCaName(url)
+
+		if err != nil || caCertPool == nil {
+			return nil, err
+		}
+
+		setCaCertPoolInMap(url, *caCertPool)
+
+		return caCertPool, nil
+	}
+}
+
+func getCaCertPoolFromMap(key string) (x509.CertPool, bool) {
+	mu.RLock()
+	defer mu.RUnlock()
+	caCertPool, ok := caMap[key]
+	return caCertPool, ok
+}
+
+func setCaCertPoolInMap(key string, caCertPool x509.CertPool) {
+	mu.Lock()
+	defer mu.Unlock()
+	caMap[key] = caCertPool
+}
+
+func buildCertPoolForCaName(url string) (*x509.CertPool, error) {
+	data, err := FetchDataFromGetIssuerPki(url)
+
+	if err != nil {
+		return nil, err
+	}
+
+	// Create a CertPool
 	caCertPool, err := x509.SystemCertPool()
+
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/util/ready/ready.go b/pkg/util/ready/ready.go
index 67e7d841d..cb1540b3b 100644
--- a/pkg/util/ready/ready.go
+++ b/pkg/util/ready/ready.go
@@ -5,7 +5,6 @@ package ready
 
 import (
 	"context"
-	"fmt"
 	"net"
 
 	mcv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1"
@@ -194,75 +193,3 @@ func CheckMachineConfigPoolIsReady(ctx context.Context, cli mcoclientv1.MachineC
 		return MachineConfigPoolIsReady(s), nil
 	}
 }
-
-type MCPLister interface {
-	List(ctx context.Context, opts metav1.ListOptions) (*mcv1.MachineConfigPoolList, error)
-}
-
-type NodeLister interface {
-	List(ctx context.Context, opts metav1.ListOptions) (*corev1.NodeList, error)
-}
-
-// SameNumberOfNodesAndMachines returns true if the cluster has the same number of nodes and total machines,
-// and an error if any. Otherwise it returns false and nil.
-func SameNumberOfNodesAndMachines(ctx context.Context, mcpLister MCPLister, nodeLister NodeLister) (bool, error) {
-	if mcpLister == nil {
-		return false, fmt.Errorf("mcpLister is nil")
-	}
-
-	if nodeLister == nil {
-		return false, fmt.Errorf("nodeLister is nil")
-	}
-
-	machineConfigPoolList, err := mcpLister.List(ctx, metav1.ListOptions{})
-	if err != nil {
-		return false, err
-	}
-
-	totalMachines, err := TotalMachinesInTheMCPs(machineConfigPoolList.Items)
-	if err != nil {
-		return false, err
-	}
-
-	nodes, err := nodeLister.List(ctx, metav1.ListOptions{})
-	if err != nil {
-		return false, err
-	}
-
-	nNodes := len(nodes.Items)
-	if nNodes != totalMachines {
-		return false, fmt.Errorf("cluster has %d nodes but %d under MCPs, not removing private DNS zone", nNodes, totalMachines)
-	}
-
-	return true, nil
-}
-
-// TotalMachinesInTheMCPs returns the total number of machines in the machineConfigPools
-// and an error, if any.
-func TotalMachinesInTheMCPs(machineConfigPools []mcv1.MachineConfigPool) (int, error) {
-	totalMachines := 0
-	for _, mcp := range machineConfigPools {
-		if !MCPContainsARODNSConfig(mcp) {
-			return 0, fmt.Errorf("ARO DNS config not found in MCP %s", mcp.Name)
-		}
-
-		if !MachineConfigPoolIsReady(&mcp) {
-			return 0, fmt.Errorf("MCP %s not ready", mcp.Name)
-		}
-
-		totalMachines += int(mcp.Status.MachineCount)
-	}
-	return totalMachines, nil
-}
-
-func MCPContainsARODNSConfig(mcp mcv1.MachineConfigPool) bool {
-	for _, source := range mcp.Status.Configuration.Source {
-		mcpPrefix := "99-"
-		mcpSuffix := "-aro-dns"
-
-		if source.Name == mcpPrefix+mcp.Name+mcpSuffix {
-			return true
-		}
-	}
-	return false
-}
diff --git a/pkg/util/version/clusterversion.go b/pkg/util/version/clusterversion.go
index 2316b1b33..370f294c2 100644
--- a/pkg/util/version/clusterversion.go
+++ b/pkg/util/version/clusterversion.go
@@ -4,12 +4,9 @@ package version
 // Licensed under the Apache License 2.0.
 
 import (
-	"context"
 	"errors"
 
 	configv1 "github.com/openshift/api/config/v1"
-	configclient "github.com/openshift/client-go/config/clientset/versioned"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 // GetClusterVersion fetches the version of the openshift cluster.
@@ -37,17 +34,3 @@ func GetClusterVersion(cv *configv1.ClusterVersion) (*Version, error) {
 
 	return nil, unknownErr
 }
-
-func ClusterVersionIsLessThan4_4(ctx context.Context, configcli configclient.Interface) (bool, error) {
-	cv, err := configcli.ConfigV1().ClusterVersions().Get(ctx, "version", metav1.GetOptions{})
-	if err != nil {
-		return false, err
-	}
-	v, err := GetClusterVersion(cv)
-	if err != nil {
-		return false, err
-	}
-
-	// 4.3 uses SRV records for etcd
-	return v.Lt(NewVersion(4, 4)), nil
-}
diff --git a/pkg/validate/dynamic/dynamic.go b/pkg/validate/dynamic/dynamic.go
index ba87cfa16..3a9e343ef 100644
--- a/pkg/validate/dynamic/dynamic.go
+++ b/pkg/validate/dynamic/dynamic.go
@@ -467,7 +467,8 @@ func createAuthorizationRequest(subject, resourceId string, actions ...string) r
 	return remotepdp.AuthorizationRequest{
 		Subject: remotepdp.SubjectInfo{
 			Attributes: remotepdp.SubjectAttributes{
-				ObjectId: subject,
+				ObjectId:  subject,
+				ClaimName: remotepdp.GroupExpansion, // always do group expansion
 			},
 		},
 		Actions: actionInfos,


Test patch:
diff --git a/.pipelines/e2e.yml b/.pipelines/e2e.yml
index 94328e1dd..6a21ab71f 100644
--- a/.pipelines/e2e.yml
+++ b/.pipelines/e2e.yml
@@ -52,6 +52,7 @@ jobs:
 
           export CI=true
           . ./hack/e2e/run-rp-and-e2e.sh
+          get_cluster_sp
           deploy_e2e_db
         displayName: Setup (Azure)
 
diff --git a/hack/e2e/run-rp-and-e2e.sh b/hack/e2e/run-rp-and-e2e.sh
index 5dc187864..aee67f90b 100755
--- a/hack/e2e/run-rp-and-e2e.sh
+++ b/hack/e2e/run-rp-and-e2e.sh
@@ -211,9 +211,30 @@ delete_e2e_cluster() {
         ./cluster delete
     else
         go run ./hack/cluster delete
+        go run ./hack/cluster deleteApp
     fi
 }
 
+get_cluster_sp() {
+    echo "########## Downloading SP secrets ##########"
+    az keyvault secret download --vault-name=$CSP_VAULT_NAME \
+        --name=aro-v4-e2e-devops-spn-1-app-id \
+        --file=secrets/app-id
+    az keyvault secret download --vault-name=$CSP_VAULT_NAME \
+        --name=aro-v4-e2e-devops-spn-1-sp-id \
+        --file=secrets/sp-id
+    az keyvault secret download --vault-name=$CSP_VAULT_NAME \
+        --name=aro-v4-e2e-devops-spn-1-secret-value \
+        --file=secrets/secret-value
+
+    echo -e -n "\nexport AZURE_CLUSTER_SERVICE_PRINCIPAL_ID=" >>secrets/env
+    cat secrets/sp-id >>secrets/env
+    echo -e -n "\nexport AZURE_CLUSTER_APP_ID=" >>secrets/env
+    cat secrets/app-id >>secrets/env
+    echo -e -n "\nexport AZURE_CLUSTER_APP_SECRET=" >>secrets/env
+    cat secrets/secret-value >>secrets/env
+}
+
 # TODO: CLUSTER and is also recalculated in multiple places
 # in the billing pipelines :-(
 
diff --git a/pkg/cluster/removeprivatednszone_test.go b/pkg/cluster/removeprivatednszone_test.go
index a454989bf..cf452e534 100644
--- a/pkg/cluster/removeprivatednszone_test.go
+++ b/pkg/cluster/removeprivatednszone_test.go
@@ -5,46 +5,348 @@ package cluster
 
 import (
 	"context"
-	"errors"
 	"testing"
 
+	mgmtprivatedns "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns"
+	"github.com/Azure/go-autorest/autorest/to"
 	"github.com/golang/mock/gomock"
+	configv1 "github.com/openshift/api/config/v1"
+	configclient "github.com/openshift/client-go/config/clientset/versioned"
+	configfake "github.com/openshift/client-go/config/clientset/versioned/fake"
+	mcv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1"
+	mcoclient "github.com/openshift/machine-config-operator/pkg/generated/clientset/versioned"
+	mcofake "github.com/openshift/machine-config-operator/pkg/generated/clientset/versioned/fake"
 	"github.com/sirupsen/logrus"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/client-go/kubernetes"
+	"k8s.io/client-go/kubernetes/fake"
 
 	"github.com/Azure/ARO-RP/pkg/api"
 	mock_privatedns "github.com/Azure/ARO-RP/pkg/util/mocks/azureclient/mgmt/privatedns"
-	utilerror "github.com/Azure/ARO-RP/test/util/error"
 )
 
 func TestRemovePrivateDNSZone(t *testing.T) {
 	ctx := context.Background()
 	const resourceGroupID = "/subscriptions/0000000-0000-0000-0000-000000000000/resourceGroups/testGroup"
 
-	t.Run("should return nil when privateZones.ListByResourceGroup() returns an error", func(t *testing.T) {
-		controller := gomock.NewController(t)
-		defer controller.Finish()
+	for _, tt := range []struct {
+		name                      string
+		doc                       *api.OpenShiftClusterDocument
+		mocks                     func(*mock_privatedns.MockPrivateZonesClient, *mock_privatedns.MockVirtualNetworkLinksClient)
+		kubernetescli             kubernetes.Interface
+		mcocli                    mcoclient.Interface
+		configcli                 configclient.Interface
+		wantDNSPrivateZoneRemoved bool
+	}{
+		{
+			name: "no private zones",
+			doc: &api.OpenShiftClusterDocument{
+				OpenShiftCluster: &api.OpenShiftCluster{
+					Properties: api.OpenShiftClusterProperties{
+						ClusterProfile: api.ClusterProfile{
+							ResourceGroupID: resourceGroupID,
+						},
+					},
+				},
+			},
+			mocks: func(privateZones *mock_privatedns.MockPrivateZonesClient, virtualNetworkLinks *mock_privatedns.MockVirtualNetworkLinksClient) {
+				privateZones.EXPECT().
+					ListByResourceGroup(ctx, "testGroup", nil).
+					Return(nil, nil)
+			},
+			configcli: configfake.NewSimpleClientset(
+				&configv1.DNS{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "cluster",
+					},
+					Spec: configv1.DNSSpec{
+						PrivateZone: &configv1.DNSZone{
+							ID: "/subscriptions/0000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.Network/privateDnsZones/zone1",
+						},
+					},
+				},
+			),
+			wantDNSPrivateZoneRemoved: true,
+		},
+		{
+			name: "has private zone, dnsmasq config not yet reconciled",
+			doc: &api.OpenShiftClusterDocument{
+				OpenShiftCluster: &api.OpenShiftCluster{
+					Properties: api.OpenShiftClusterProperties{
+						ClusterProfile: api.ClusterProfile{
+							ResourceGroupID: resourceGroupID,
+						},
+					},
+				},
+			},
+			mocks: func(privateZones *mock_privatedns.MockPrivateZonesClient, virtualNetworkLinks *mock_privatedns.MockVirtualNetworkLinksClient) {
+				privateZones.EXPECT().
+					ListByResourceGroup(ctx, "testGroup", nil).
+					Return([]mgmtprivatedns.PrivateZone{
+						{
+							ID: to.StringPtr("/subscriptions/0000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.Network/privateDnsZones/zone1"),
+						},
+					}, nil)
+			},
+			mcocli: mcofake.NewSimpleClientset(
+				&mcv1.MachineConfigPool{},
+			),
+		},
+		{
+			name: "has private zone, pool not yet ready",
+			doc: &api.OpenShiftClusterDocument{
+				OpenShiftCluster: &api.OpenShiftCluster{
+					Properties: api.OpenShiftClusterProperties{
+						ClusterProfile: api.ClusterProfile{
+							ResourceGroupID: resourceGroupID,
+						},
+					},
+				},
+			},
+			mocks: func(privateZones *mock_privatedns.MockPrivateZonesClient, virtualNetworkLinks *mock_privatedns.MockVirtualNetworkLinksClient) {
+				privateZones.EXPECT().
+					ListByResourceGroup(ctx, "testGroup", nil).
+					Return([]mgmtprivatedns.PrivateZone{
+						{
+							ID: to.StringPtr("/subscriptions/0000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.Network/privateDnsZones/zone1"),
+						},
+					}, nil)
+			},
+			mcocli: mcofake.NewSimpleClientset(
+				&mcv1.MachineConfigPool{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "master",
+					},
+					Status: mcv1.MachineConfigPoolStatus{
+						Configuration: mcv1.MachineConfigPoolStatusConfiguration{
+							Source: []corev1.ObjectReference{
+								{
+									Name: "99-master-aro-dns",
+								},
+							},
+						},
+						MachineCount: 1,
+					},
+				},
+			),
+		},
+		{
+			name: "has private zone, node mismatch",
+			doc: &api.OpenShiftClusterDocument{
+				OpenShiftCluster: &api.OpenShiftCluster{
+					Properties: api.OpenShiftClusterProperties{
+						ClusterProfile: api.ClusterProfile{
+							ResourceGroupID: resourceGroupID,
+						},
+					},
+				},
+			},
+			mocks: func(privateZones *mock_privatedns.MockPrivateZonesClient, virtualNetworkLinks *mock_privatedns.MockVirtualNetworkLinksClient) {
+				privateZones.EXPECT().
+					ListByResourceGroup(ctx, "testGroup", nil).
+					Return([]mgmtprivatedns.PrivateZone{
+						{
+							ID: to.StringPtr("/subscriptions/0000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.Network/privateDnsZones/zone1"),
+						},
+					}, nil)
+			},
+			kubernetescli: fake.NewSimpleClientset(
+				&corev1.Node{},
+			),
+			mcocli: mcofake.NewSimpleClientset(
+				&mcv1.MachineConfigPool{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "master",
+					},
+					Status: mcv1.MachineConfigPoolStatus{
+						Configuration: mcv1.MachineConfigPoolStatusConfiguration{
+							Source: []corev1.ObjectReference{
+								{
+									Name: "99-master-aro-dns",
+								},
+							},
+						},
+					},
+				},
+			),
+		},
+		{
+			name: "has private zone, nodes match, 4.3, dnsmasq rolled out",
+			doc: &api.OpenShiftClusterDocument{
+				OpenShiftCluster: &api.OpenShiftCluster{
+					Properties: api.OpenShiftClusterProperties{
+						ClusterProfile: api.ClusterProfile{
+							ResourceGroupID: resourceGroupID,
+						},
+					},
+				},
+			},
+			mocks: func(privateZones *mock_privatedns.MockPrivateZonesClient, virtualNetworkLinks *mock_privatedns.MockVirtualNetworkLinksClient) {
+				privateZones.EXPECT().
+					ListByResourceGroup(ctx, "testGroup", nil).
+					Return([]mgmtprivatedns.PrivateZone{
+						{
+							ID: to.StringPtr("/subscriptions/0000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.Network/privateDnsZones/zone1"),
+						},
+					}, nil)
+			},
+			kubernetescli: fake.NewSimpleClientset(
+				&corev1.Node{},
+			),
+			mcocli: mcofake.NewSimpleClientset(
+				&mcv1.MachineConfigPool{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "master",
+					},
+					Status: mcv1.MachineConfigPoolStatus{
+						Configuration: mcv1.MachineConfigPoolStatusConfiguration{
+							Source: []corev1.ObjectReference{
+								{
+									Name: "99-master-aro-dns",
+								},
+							},
+						},
+						MachineCount:        1,
+						UpdatedMachineCount: 1,
+						ReadyMachineCount:   1,
+					},
+				},
+			),
+			configcli: configfake.NewSimpleClientset(
+				&configv1.ClusterVersion{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "version",
+					},
+					Status: configv1.ClusterVersionStatus{
+						History: []configv1.UpdateHistory{
+							{
+								State:   configv1.CompletedUpdate,
+								Version: "4.3.999",
+							},
+						},
+					},
+				},
+			),
+		},
+		{
+			name: "has private zone, nodes match, 4.4, dnsmasq rolled out",
+			doc: &api.OpenShiftClusterDocument{
+				OpenShiftCluster: &api.OpenShiftCluster{
+					Properties: api.OpenShiftClusterProperties{
+						ClusterProfile: api.ClusterProfile{
+							ResourceGroupID: resourceGroupID,
+						},
+					},
+				},
+			},
+			mocks: func(privateZones *mock_privatedns.MockPrivateZonesClient, virtualNetworkLinks *mock_privatedns.MockVirtualNetworkLinksClient) {
+				privateZones.EXPECT().
+					ListByResourceGroup(ctx, "testGroup", nil).
+					Return([]mgmtprivatedns.PrivateZone{
+						{
+							ID: to.StringPtr("/subscriptions/0000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.Network/privateDnsZones/zone1"),
+						},
+					}, nil)
 
-		privateZones := mock_privatedns.NewMockPrivateZonesClient(controller)
-		privateZones.EXPECT().ListByResourceGroup(ctx, gomock.Any(), nil).Return(nil, errors.New("someError"))
+				virtualNetworkLinks.EXPECT().
+					List(ctx, "testGroup", "zone1", nil).
+					Return([]mgmtprivatedns.VirtualNetworkLink{
+						{
+							Name: to.StringPtr("link1"),
+						},
+					}, nil)
 
-		doc := &api.OpenShiftClusterDocument{
-			OpenShiftCluster: &api.OpenShiftCluster{
-				Properties: api.OpenShiftClusterProperties{
-					ClusterProfile: api.ClusterProfile{
-						ResourceGroupID: resourceGroupID,
+				virtualNetworkLinks.EXPECT().
+					DeleteAndWait(ctx, "testGroup", "zone1", "link1", "").
+					Return(nil)
+
+				privateZones.EXPECT().
+					DeleteAndWait(ctx, "testGroup", "zone1", "").
+					Return(nil)
+			},
+			kubernetescli: fake.NewSimpleClientset(
+				&corev1.Node{},
+			),
+			mcocli: mcofake.NewSimpleClientset(
+				&mcv1.MachineConfigPool{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "master",
+					},
+					Status: mcv1.MachineConfigPoolStatus{
+						Configuration: mcv1.MachineConfigPoolStatusConfiguration{
+							Source: []corev1.ObjectReference{
+								{
+									Name: "99-master-aro-dns",
+								},
+							},
+						},
+						MachineCount:        1,
+						UpdatedMachineCount: 1,
+						ReadyMachineCount:   1,
 					},
 				},
-			},
-		}
-		m := &manager{
-			doc:          doc,
-			log:          logrus.NewEntry(logrus.StandardLogger()),
-			privateZones: privateZones,
-		}
+			),
+			configcli: configfake.NewSimpleClientset(
+				&configv1.ClusterVersion{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "version",
+					},
+					Status: configv1.ClusterVersionStatus{
+						History: []configv1.UpdateHistory{
+							{
+								State:   configv1.CompletedUpdate,
+								Version: "4.4.0",
+							},
+						},
+					},
+				},
+				&configv1.DNS{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "cluster",
+					},
+					Spec: configv1.DNSSpec{
+						PrivateZone: &configv1.DNSZone{
+							ID: "/subscriptions/0000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.Network/privateDnsZones/zone1",
+						},
+					},
+				},
+			),
+			wantDNSPrivateZoneRemoved: true,
+		},
+	} {
+		t.Run(tt.name, func(t *testing.T) {
+			controller := gomock.NewController(t)
+			defer controller.Finish()
+
+			privateZones := mock_privatedns.NewMockPrivateZonesClient(controller)
+			virtualNetworkLinks := mock_privatedns.NewMockVirtualNetworkLinksClient(controller)
+			tt.mocks(privateZones, virtualNetworkLinks)
+
+			m := &manager{
+				log:                 logrus.NewEntry(logrus.StandardLogger()),
+				doc:                 tt.doc,
+				privateZones:        privateZones,
+				virtualNetworkLinks: virtualNetworkLinks,
+				kubernetescli:       tt.kubernetescli,
+				mcocli:              tt.mcocli,
+				configcli:           tt.configcli,
+			}
 
-		err := m.removePrivateDNSZone(ctx)
+			err := m.removePrivateDNSZone(ctx)
+			if err != nil {
+				t.Fatal(err)
+			}
 
-		expectedError := ""
-		utilerror.AssertErrorMessage(t, err, expectedError)
-	})
+			if tt.wantDNSPrivateZoneRemoved {
+				dns, err := m.configcli.ConfigV1().DNSes().Get(ctx, "cluster", metav1.GetOptions{})
+				if err != nil {
+					t.Fatal(err)
+				}
+				if dns.Spec.PrivateZone != nil {
+					t.Error(dns.Spec.PrivateZone)
+				}
+			}
+		})
+	}
 }
diff --git a/pkg/util/azureclient/roundtripper_test.go b/pkg/util/azureclient/roundtripper_test.go
deleted file mode 100644
index 791ed0000..000000000
--- a/pkg/util/azureclient/roundtripper_test.go
+++ /dev/null
@@ -1,160 +0,0 @@
-package azureclient
-
-// Copyright (c) Microsoft Corporation.
-// Licensed under the Apache License 2.0.
-
-import (
-	"fmt"
-	"net/http"
-	"net/url"
-	"testing"
-	"time"
-
-	"github.com/sirupsen/logrus"
-
-	"github.com/Azure/ARO-RP/pkg/api"
-	utillog "github.com/Azure/ARO-RP/pkg/util/log"
-)
-
-// fieldsContainsSubfields returns true if all the keys of subfields are contained in fields and the value
-// for each key is the same in fields and in subfields.
-func fieldsContainsSubfields(fields logrus.Fields, subfields logrus.Fields) (containsSubfields bool) {
-	for subfieldsKey := range subfields {
-		fieldsValue, subfieldsKeyIsInFields := fields[subfieldsKey]
-		if !subfieldsKeyIsInFields {
-			fmt.Printf("fields does not contain subfieldsKey %v\n", subfieldsKey)
-			return false
-		}
-
-		if fieldsValue != subfields[subfieldsKey] {
-			fmt.Printf("fields value %v != subfields value %v\n", fieldsValue, subfields[subfieldsKey])
-			return false
-		}
-	}
-	return true
-}
-
-func TestUpdateCorrelationDataAndEnrichLogWithRequest(t *testing.T) {
-	type testCase struct {
-		name              string
-		correlationData   *api.CorrelationData
-		req               *http.Request
-		expectedSubfields logrus.Fields
-	}
-
-	startTime := time.Now()
-	url, err := url.Parse("https://example.com/foo%2fbar")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	testcases := []testCase{
-		{
-			name: "updateCorrelationDataAndEnrichLogWithReq returns appropriate logrus.Entry",
-			correlationData: &api.CorrelationData{
-				ClientRequestID: "ClientRequestID",
-				CorrelationID:   "CorrelationID",
-				RequestID:       "random_request_id",
-				OperationID:     "random_operation_id",
-			},
-			req: &http.Request{
-				URL: url,
-			},
-			expectedSubfields: logrus.Fields{
-				"client_request_id": "ClientRequestID",
-				"correlation_id":    "CorrelationID",
-				"request_time":      startTime,
-				"request_id":        "random_request_id",
-				"LOGKIND":           "outboundRequests",
-			},
-		},
-	}
-	for _, tc := range testcases {
-		t.Run(tc.name, func(t *testing.T) {
-			baseLogger := utillog.GetLogger()
-			l := updateCorrelationDataAndEnrichLogWithRequest(tc.correlationData, baseLogger, startTime, tc.req)
-			if tc.correlationData.RequestTime != startTime {
-				t.Fatal("wrong RequestTime registration")
-			}
-
-			operationId, ok := l.Data["operation_id"].(string)
-			if !ok {
-				t.Fatal("generated operation id is not of string type")
-			}
-
-			if operationId == "" {
-				t.Fatal("generated operationId should not be empty")
-			}
-
-			if !fieldsContainsSubfields(l.Data, tc.expectedSubfields) {
-				t.Fatal("logrus entry does not contain all expectedSubfields")
-			}
-		})
-	}
-}
-
-func TestUpdateCorrelationDataAndEnrichLogWithResponse(t *testing.T) {
-	type testCase struct {
-		name                         string
-		res                          *http.Response
-		correlationData              *api.CorrelationData
-		expecetdCorrelationIDUpdated string
-		expectedSubfields            logrus.Fields
-	}
-
-	startTime := time.Now()
-	testcases := []testCase{
-		{
-			name: "updateCorrelationDataAndEnrichLogWithResponse returns appropriate logrus.Entry when response is nil",
-			res:  nil,
-			correlationData: &api.CorrelationData{
-				ClientRequestID: "ClientRequestID",
-				CorrelationID:   "CorrelationID",
-				RequestID:       "random_request_id",
-				OperationID:     "random_operation_id",
-				RequestTime:     startTime,
-			},
-			expecetdCorrelationIDUpdated: "CorrelationID",
-			expectedSubfields: logrus.Fields{
-				"response_status_code":  "0",
-				"duration_milliseconds": time.Since(startTime).Milliseconds(),
-			},
-		},
-		{
-			name: "updateCorrelationDataAndEnrichLogWithResponse returns appropriate logrus.Entry when response is not nil",
-			correlationData: &api.CorrelationData{
-				ClientRequestID: "ClientRequestID",
-				CorrelationID:   "CorrelationID",
-				RequestID:       "random_request_id",
-				OperationID:     "random_operation_id",
-				RequestTime:     startTime,
-			},
-			expecetdCorrelationIDUpdated: "the_correlation_request_id",
-			res: &http.Response{
-				StatusCode:    http.StatusOK,
-				Header:        http.Header{"X-Ms-Correlation-Request-Id": []string{"the_correlation_request_id"}},
-				ContentLength: int64(10),
-			},
-			expectedSubfields: logrus.Fields{
-				"response_status_code": http.StatusOK,
-				"correlation_id":       "the_correlation_request_id",
-				"content_length":       int64(10),
-			},
-		},
-	}
-	for _, tc := range testcases {
-		t.Run(tc.name, func(t *testing.T) {
-			baseLogger := utillog.GetLogger()
-
-			l := updateCorrelationDataAndEnrichLogWithResponse(tc.correlationData, baseLogger, tc.res, startTime)
-
-			if tc.correlationData.CorrelationID != tc.expecetdCorrelationIDUpdated {
-				t.Fatal("correlationData.CorrelationID not updated properly")
-			}
-
-			if !fieldsContainsSubfields(l.Data, tc.expectedSubfields) {
-				t.Fatal("logrus entry does not contain all expectedSubfields")
-			}
-		})
-	}
-}
diff --git a/pkg/util/net/net_test.go b/pkg/util/net/net_test.go
deleted file mode 100644
index d2b7e8ba5..000000000
--- a/pkg/util/net/net_test.go
+++ /dev/null
@@ -1,360 +0,0 @@
-package net
-
-// Copyright (c) Microsoft Corporation.
-// Licensed under the Apache License 2.0.
-
-import (
-	"context"
-	"errors"
-	"testing"
-
-	mgmtprivatedns "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns"
-	"github.com/Azure/go-autorest/autorest/to"
-	"github.com/golang/mock/gomock"
-	configv1 "github.com/openshift/api/config/v1"
-	configclient "github.com/openshift/client-go/config/clientset/versioned"
-	configfake "github.com/openshift/client-go/config/clientset/versioned/fake"
-	mcv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1"
-	mcoclient "github.com/openshift/machine-config-operator/pkg/generated/clientset/versioned"
-	mcofake "github.com/openshift/machine-config-operator/pkg/generated/clientset/versioned/fake"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/client-go/kubernetes"
-	"k8s.io/client-go/kubernetes/fake"
-
-	"github.com/Azure/ARO-RP/pkg/api"
-	utillog "github.com/Azure/ARO-RP/pkg/util/log"
-	mock_privatedns "github.com/Azure/ARO-RP/pkg/util/mocks/azureclient/mgmt/privatedns"
-	mock_net "github.com/Azure/ARO-RP/pkg/util/mocks/net"
-	utilerror "github.com/Azure/ARO-RP/test/util/error"
-)
-
-var (
-	resourceGroupName = "testGroup"
-	subscriptionID    = "0000000-0000-0000-0000-000000000000"
-	resourceGroupID   = "/subscriptions/" + subscriptionID + "/resourceGroups/" + resourceGroupName
-	vnetName          = "testVnet"
-	resourceID        = resourceGroupID + "/providers/Microsoft.Network/virtualNetworks/" + vnetName
-)
-
-const id = "/subscriptions/0000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Microsoft.Network/privateDnsZones/zone1"
-
-func TestUpdateClusterDNSFn(t *testing.T) {
-	type testCase struct {
-		name                string
-		resourceGroupID     string
-		wantErr             string
-		ensureMocksBehavior func(dnsI mock_net.MockDNSIClient)
-	}
-
-	testcases := []testCase{
-		{
-			name:    "should propagate the error from dnsi.Get",
-			wantErr: "some error",
-			ensureMocksBehavior: func(dnsI mock_net.MockDNSIClient) {
-				dnsI.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("some error"))
-			},
-		},
-		{
-			name:            "should not call dnsI.Update when private zone id does not have the resource group id as prefix",
-			resourceGroupID: "rg",
-			ensureMocksBehavior: func(dnsI mock_net.MockDNSIClient) {
-				v := &configv1.DNS{
-					Spec: configv1.DNSSpec{
-						PrivateZone: &configv1.DNSZone{
-							ID: "not_same_rg_the_id",
-						},
-					},
-				}
-				dnsI.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(v, nil)
-				dnsI.EXPECT().Update(gomock.Any(), gomock.Any(), gomock.Any()).Times(0)
-			},
-		},
-		{
-			name:            "should call dnsI.Update with Spec.PrivateZone equal to nil",
-			resourceGroupID: "rg_",
-			ensureMocksBehavior: func(dnsI mock_net.MockDNSIClient) {
-				v := configv1.DNS{
-					Spec: configv1.DNSSpec{
-						PrivateZone: &configv1.DNSZone{
-							ID: "rg_the_id",
-						},
-					},
-				}
-
-				dnsI.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(&v, nil)
-				dnsI.EXPECT().Update(gomock.Any(), MatchesNilPrivateZone{}, gomock.Any())
-			},
-		},
-	}
-
-	for _, tc := range testcases {
-		t.Run(tc.name, func(t *testing.T) {
-			controller := gomock.NewController(t)
-			defer controller.Finish()
-			dnsI := mock_net.NewMockDNSIClient(controller)
-
-			if tc.ensureMocksBehavior != nil {
-				tc.ensureMocksBehavior(*dnsI)
-			}
-
-			fn := updateClusterDNSFn(context.Background(), dnsI, tc.resourceGroupID)
-			err := fn()
-			utilerror.AssertErrorMessage(t, err, tc.wantErr)
-		})
-	}
-
-	t.Run("dnsI is nil", func(t *testing.T) {
-		fn := updateClusterDNSFn(context.Background(), nil, "")
-		err := fn()
-		utilerror.AssertErrorMessage(t, err, "dnsClient interface is nil")
-	})
-}
-
-type MatchesNilPrivateZone struct {
-}
-
-func (MatchesNilPrivateZone) Matches(x interface{}) bool {
-	arg, ok := x.(*configv1.DNS)
-	if !ok {
-		return false
-	}
-
-	return arg.Spec.PrivateZone == nil
-}
-
-func (MatchesNilPrivateZone) String() string {
-	return "arg.Spec.PrivateZone does not match (is not nil)"
-}
-
-func TestDeletePrivateDNSVNetLinks(t *testing.T) {
-	type testCase struct {
-		name                string
-		resourceID          string
-		wantErr             string
-		ensureMocksBehavior func(vNetLinksClient *mock_privatedns.MockVirtualNetworkLinksClient)
-	}
-	testcases := []testCase{
-		{
-			name:       "propagates invalid resource id error",
-			resourceID: "invalid_resourceId",
-			wantErr:    "parsing failed for invalid_resourceId. Invalid resource Id format",
-			ensureMocksBehavior: func(vNetLinksClient *mock_privatedns.MockVirtualNetworkLinksClient) {
-				vNetLinksClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
-					Return(nil, errors.New("parsing failed for invalid_resourceId. Invalid resource Id format"))
-			},
-		},
-		{
-			name:    "propagates error from vNetLinksClient.List",
-			wantErr: "some_error",
-			ensureMocksBehavior: func(vNetLinksClient *mock_privatedns.MockVirtualNetworkLinksClient) {
-				vNetLinksClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("some_error"))
-			},
-		},
-		{
-			name:    "ppropagates error from vNetLinksClient.DeleteAndWait",
-			wantErr: "some_error",
-			ensureMocksBehavior: func(vNetLinksClient *mock_privatedns.MockVirtualNetworkLinksClient) {
-				name := "name"
-				listResult := []mgmtprivatedns.VirtualNetworkLink{{Name: &name}}
-				vNetLinksClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(listResult, nil)
-				vNetLinksClient.EXPECT().DeleteAndWait(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("some_error"))
-			},
-		},
-		{
-			name: "returns nil when no errors found",
-			ensureMocksBehavior: func(vNetLinksClient *mock_privatedns.MockVirtualNetworkLinksClient) {
-				name := "name"
-				listResult := []mgmtprivatedns.VirtualNetworkLink{{Name: &name}}
-				vNetLinksClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(listResult, nil)
-				vNetLinksClient.EXPECT().DeleteAndWait(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
-			},
-		},
-	}
-	for _, tc := range testcases {
-		t.Run(tc.name, func(t *testing.T) {
-			controller := gomock.NewController(t)
-			defer controller.Finish()
-			vNetLinksClient := mock_privatedns.NewMockVirtualNetworkLinksClient(controller)
-
-			if tc.ensureMocksBehavior != nil {
-				tc.ensureMocksBehavior(vNetLinksClient)
-			}
-
-			err := DeletePrivateDNSVNetLinks(context.Background(), vNetLinksClient, resourceID)
-			utilerror.AssertErrorMessage(t, err, tc.wantErr)
-		})
-	}
-}
-
-func TestRemovePrivateDNSZone(t *testing.T) {
-	privateZone := []mgmtprivatedns.PrivateZone{{ID: to.StringPtr(id)}}
-
-	doc := &api.OpenShiftClusterDocument{
-		OpenShiftCluster: &api.OpenShiftCluster{
-			Properties: api.OpenShiftClusterProperties{
-				ClusterProfile: api.ClusterProfile{ResourceGroupID: resourceGroupID},
-			},
-		},
-	}
-
-	dns := &configv1.DNS{
-		ObjectMeta: metav1.ObjectMeta{Name: "cluster"},
-		Spec: configv1.DNSSpec{
-			PrivateZone: &configv1.DNSZone{ID: id},
-		},
-	}
-
-	mcp := &mcv1.MachineConfigPool{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: "master",
-		},
-		Status: mcv1.MachineConfigPoolStatus{
-			Configuration: mcv1.MachineConfigPoolStatusConfiguration{
-				Source: []corev1.ObjectReference{{Name: "99-master-aro-dns"}},
-			},
-			MachineCount: 1,
-		},
-	}
-
-	ctx := context.Background()
-
-	for _, tt := range []struct {
-		name                      string
-		doc                       *api.OpenShiftClusterDocument
-		mocks                     func(*mock_privatedns.MockPrivateZonesClient, *mock_privatedns.MockVirtualNetworkLinksClient)
-		kubernetescli             kubernetes.Interface
-		mcocli                    mcoclient.Interface
-		configcli                 configclient.Interface
-		wantDNSPrivateZoneRemoved bool
-		wantError                 string
-	}{
-		{
-			name: "no private zones",
-			doc:  doc,
-			mocks: func(privateZones *mock_privatedns.MockPrivateZonesClient, virtualNetworkLinks *mock_privatedns.MockVirtualNetworkLinksClient) {
-				privateZones.EXPECT().ListByResourceGroup(ctx, "testGroup", nil).Return(nil, nil)
-			},
-			configcli:                 configfake.NewSimpleClientset(dns),
-			wantDNSPrivateZoneRemoved: true,
-		},
-		{
-			name: "has private zone, dnsmasq config not yet reconciled",
-			doc:  doc,
-			mocks: func(privateZones *mock_privatedns.MockPrivateZonesClient, virtualNetworkLinks *mock_privatedns.MockVirtualNetworkLinksClient) {
-				privateZones.EXPECT().ListByResourceGroup(ctx, "testGroup", nil).Return([]mgmtprivatedns.PrivateZone{{ID: to.StringPtr(id)}}, nil)
-			},
-			mcocli:    mcofake.NewSimpleClientset(&mcv1.MachineConfigPool{}),
-			configcli: configfake.NewSimpleClientset(),
-			kubernetescli: fake.NewSimpleClientset(
-				&corev1.Node{},
-			),
-		},
-		{
-			name: "has private zone, pool not yet ready",
-			doc:  doc,
-			mocks: func(privateZones *mock_privatedns.MockPrivateZonesClient, virtualNetworkLinks *mock_privatedns.MockVirtualNetworkLinksClient) {
-				privateZones.EXPECT().ListByResourceGroup(ctx, "testGroup", nil).Return(privateZone, nil)
-			},
-			mcocli: mcofake.NewSimpleClientset(mcp),
-			kubernetescli: fake.NewSimpleClientset(
-				&corev1.Node{},
-			),
-			wantError: "configcli is nil",
-		},
-		{
-			name: "has private zone, nodes match, 4.4, dnsmasq rolled out",
-			doc:  doc,
-			mocks: func(privateZones *mock_privatedns.MockPrivateZonesClient, virtualNetworkLinks *mock_privatedns.MockVirtualNetworkLinksClient) {
-				privateZones.EXPECT().ListByResourceGroup(ctx, "testGroup", nil).Return(privateZone, nil)
-
-				virtualNetworkLinks.EXPECT().
-					List(ctx, "testGroup", "zone1", nil).
-					Return([]mgmtprivatedns.VirtualNetworkLink{
-						{
-							Name: to.StringPtr("link1"),
-						},
-					}, nil)
-
-				virtualNetworkLinks.EXPECT().
-					DeleteAndWait(ctx, "testGroup", "zone1", "link1", "").
-					Return(nil)
-
-				privateZones.EXPECT().
-					DeleteAndWait(ctx, "testGroup", "zone1", "").
-					Return(nil)
-			},
-			kubernetescli: fake.NewSimpleClientset(
-				&corev1.Node{},
-			),
-			mcocli: mcofake.NewSimpleClientset(
-				&mcv1.MachineConfigPool{
-					ObjectMeta: metav1.ObjectMeta{
-						Name: "master",
-					},
-					Status: mcv1.MachineConfigPoolStatus{
-						Configuration: mcv1.MachineConfigPoolStatusConfiguration{
-							Source: []corev1.ObjectReference{
-								{
-									Name: "99-master-aro-dns",
-								},
-							},
-						},
-						MachineCount:        1,
-						UpdatedMachineCount: 1,
-						ReadyMachineCount:   1,
-					},
-				},
-			),
-			configcli: configfake.NewSimpleClientset(
-				&configv1.ClusterVersion{
-					ObjectMeta: metav1.ObjectMeta{
-						Name: "version",
-					},
-					Status: configv1.ClusterVersionStatus{
-						History: []configv1.UpdateHistory{
-							{
-								State:   configv1.CompletedUpdate,
-								Version: "4.4.0",
-							},
-						},
-					},
-				},
-				dns,
-			),
-			wantDNSPrivateZoneRemoved: true,
-		},
-	} {
-		t.Run(tt.name, func(t *testing.T) {
-			controller := gomock.NewController(t)
-			defer controller.Finish()
-
-			privateZones := mock_privatedns.NewMockPrivateZonesClient(controller)
-			virtualNetworkLinks := mock_privatedns.NewMockVirtualNetworkLinksClient(controller)
-			tt.mocks(privateZones, virtualNetworkLinks)
-
-			config := PrivateZoneRemovalConfig{
-				Log:                utillog.GetLogger(),
-				PrivateZonesClient: privateZones,
-				Configcli:          tt.configcli,
-				Mcocli:             tt.mcocli,
-				Kubernetescli:      tt.kubernetescli,
-				VNetLinksClient:    virtualNetworkLinks,
-				ResourceGroupID:    resourceGroupID,
-			}
-			err := RemovePrivateDNSZone(ctx, config)
-
-			utilerror.AssertErrorMessage(t, err, tt.wantError)
-
-			if tt.wantDNSPrivateZoneRemoved {
-				dns, err := tt.configcli.ConfigV1().DNSes().Get(ctx, "cluster", metav1.GetOptions{})
-				if err != nil {
-					t.Fatal(err)
-				}
-				if dns.Spec.PrivateZone != nil {
-					t.Error(dns.Spec.PrivateZone)
-				}
-			}
-		})
-	}
-}
diff --git a/pkg/util/pki/pki_test.go b/pkg/util/pki/pki_test.go
new file mode 100644
index 000000000..c7a9d30bb
--- /dev/null
+++ b/pkg/util/pki/pki_test.go
@@ -0,0 +1,61 @@
+package pki
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the Apache License 2.0.
+
+import (
+	"crypto/tls"
+	"crypto/x509"
+	"fmt"
+	"net/http"
+	"os"
+	"testing"
+)
+
+func TestGetTlsConfig(t *testing.T) {
+	if os.Getenv("ARO_SKIP_PKI_TESTS") != "" {
+		t.Skip("")
+	}
+	kpiUrl := "https://issuer.pki.azure.com/dsms/issuercertificates?getissuersv3&caName=%s"
+	testUrl := "https://diag-runtimehost-prod.trafficmanager.net"
+
+	caName := "ame"
+	caCertPool, err := GetTlsCertPool(kpiUrl, caName)
+
+	if err != nil {
+		t.Errorf("Expected no error, got %v", err)
+	}
+
+	if caCertPool == nil {
+		t.Error("Expected non-nil CertPool")
+	}
+
+	url := fmt.Sprintf(kpiUrl, caName)
+	if _, ok := caMap[url]; !ok {
+		t.Errorf("Expected caMap to contain entry for %s", caName)
+	}
+
+	// Create a new tls.Config with the custom CA certificate
+	tlsConfig := &tls.Config{
+		RootCAs:            caCertPool,
+		InsecureSkipVerify: false,
+	}
+
+	// Use the tls.Config with your client
+	client := &http.Client{
+		Transport: &http.Transport{
+			TLSClientConfig: tlsConfig,
+		},
+	}
+
+	resp, err := client.Get(testUrl)
+
+	if err != nil {
+		if _, ok := err.(x509.UnknownAuthorityError); ok {
+			t.Errorf("Invalid SSL/TLS certificate")
+		}
+		t.Errorf("Error calling %s", testUrl)
+	}
+
+	defer resp.Body.Close()
+}
diff --git a/pkg/util/ready/ready_test.go b/pkg/util/ready/ready_test.go
index e28a6a5a5..ddcf1ca64 100644
--- a/pkg/util/ready/ready_test.go
+++ b/pkg/util/ready/ready_test.go
@@ -16,8 +16,6 @@ import (
 	kruntime "k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/client-go/kubernetes/fake"
 	ktesting "k8s.io/client-go/testing"
-
-	utilerror "github.com/Azure/ARO-RP/test/util/error"
 )
 
 func TestNodeIsReady(t *testing.T) {
@@ -652,339 +650,3 @@ func TestCheckPodsAreReadyError(t *testing.T) {
 		t.Fatalf("check pod error is: %v", err)
 	}
 }
-
-func TestTotalMachinesInTheMCPs(t *testing.T) {
-	mcpPrefix := "99-"
-	mcpSuffix := "-aro-dns"
-
-	type testCase struct {
-		name               string
-		machineConfigPools []mcv1.MachineConfigPool
-		want               int
-		wantErr            string
-	}
-	testCases := []testCase{
-		{
-			name: "totalMachinesInTheMCPs returns ARO DNS config not found error",
-			want: 0,
-			machineConfigPools: []mcv1.MachineConfigPool{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Name: "mcp_name",
-					},
-					Status: mcv1.MachineConfigPoolStatus{
-						ObservedGeneration: 0,
-						Configuration: mcv1.MachineConfigPoolStatusConfiguration{
-							Source: []corev1.ObjectReference{{Name: "non matching name"}},
-						},
-					},
-				},
-			},
-			wantErr: "ARO DNS config not found in MCP mcp_name",
-		},
-		{
-			name:    "totalMachinesInTheMCPs returns MCP is not ready error",
-			want:    0,
-			wantErr: "MCP mcp_name not ready",
-			machineConfigPools: []mcv1.MachineConfigPool{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Name:       "mcp_name",
-						Generation: 1,
-					},
-					Status: mcv1.MachineConfigPoolStatus{
-						MachineCount:        1,
-						UpdatedMachineCount: 1,
-						ReadyMachineCount:   0,
-						ObservedGeneration:  1,
-						Configuration: mcv1.MachineConfigPoolStatusConfiguration{
-							Source: []corev1.ObjectReference{{Name: mcpPrefix + "mcp_name" + mcpSuffix}},
-						},
-					},
-				},
-			},
-		},
-		{
-			name: "totalMachinesInTheMCPs returns 1",
-			want: 1,
-			machineConfigPools: []mcv1.MachineConfigPool{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Name:       "mcp_name",
-						Generation: 1,
-					},
-					Status: mcv1.MachineConfigPoolStatus{
-						MachineCount:        1,
-						UpdatedMachineCount: 1,
-						ReadyMachineCount:   1,
-						ObservedGeneration:  1,
-						Configuration: mcv1.MachineConfigPoolStatusConfiguration{
-							Source: []corev1.ObjectReference{
-								{
-									Name: mcpPrefix + "mcp_name" + mcpSuffix,
-								},
-							},
-						},
-					},
-				},
-			},
-			wantErr: "",
-		},
-		{
-			name:               "totalMachinesInTheMCPs returns 0 and no error when there are no machine config pools",
-			want:               0,
-			machineConfigPools: nil,
-			wantErr:            "",
-		},
-	}
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			got, err := TotalMachinesInTheMCPs(tc.machineConfigPools)
-			utilerror.AssertErrorMessage(t, err, tc.wantErr)
-
-			if got != tc.want {
-				t.Fatalf("expected %v, but got %v", tc.want, got)
-			}
-		})
-	}
-}
-
-func TestMcpContainsARODNSConfig(t *testing.T) {
-	mcpPrefix := "99-"
-	mcpSuffix := "-aro-dns"
-
-	type testCase struct {
-		name string
-		mcp  mcv1.MachineConfigPool
-		want bool
-	}
-
-	testcases := []testCase{
-		{
-			name: "Mcp contains ARO DNS config",
-			mcp: mcv1.MachineConfigPool{
-				ObjectMeta: metav1.ObjectMeta{
-					Name: "mcp_name",
-				},
-				Status: mcv1.MachineConfigPoolStatus{
-					ObservedGeneration: 0,
-					Configuration: mcv1.MachineConfigPoolStatusConfiguration{
-						Source: []corev1.ObjectReference{
-							{
-								Name: mcpPrefix + "mcp_name" + mcpSuffix,
-							},
-						},
-					},
-				},
-			},
-			want: true,
-		},
-		{
-			name: "Mcp does not contain ARO DNS config due to wrong source name",
-			mcp: mcv1.MachineConfigPool{
-				ObjectMeta: metav1.ObjectMeta{
-					Name: "mcp_name",
-				},
-				Status: mcv1.MachineConfigPoolStatus{
-					ObservedGeneration: 0,
-					Configuration: mcv1.MachineConfigPoolStatusConfiguration{
-						Source: []corev1.ObjectReference{
-							{
-								Name: "non matching name",
-							},
-						},
-					},
-				},
-			},
-			want: false,
-		},
-		{
-			name: "Mcp does not contain ARO DNS config due to empty status",
-			mcp: mcv1.MachineConfigPool{
-				ObjectMeta: metav1.ObjectMeta{
-					Name: "mcp_name",
-				},
-			},
-			want: false,
-		},
-	}
-
-	for _, tc := range testcases {
-		t.Run(tc.name, func(t *testing.T) {
-			got := MCPContainsARODNSConfig(tc.mcp)
-			if got != tc.want {
-				t.Fatalf("expected %v, but got %v", tc.want, got)
-			}
-		})
-	}
-}
-
-type fakeMcpLister struct {
-	expected    *mcv1.MachineConfigPoolList
-	expectedErr error
-}
-
-func (m *fakeMcpLister) List(ctx context.Context, opts metav1.ListOptions) (*mcv1.MachineConfigPoolList, error) {
-	return m.expected, m.expectedErr
-}
-
-type fakeNodeLister struct {
-	expected    *corev1.NodeList
-	expectedErr error
-}
-
-func (m *fakeNodeLister) List(ctx context.Context, opts metav1.ListOptions) (*corev1.NodeList, error) {
-	return m.expected, m.expectedErr
-}
-
-func TestSameNumberOfNodesAndMachines(t *testing.T) {
-	t.Run("should return nil mcpLister error", func(t *testing.T) {
-		got, err := SameNumberOfNodesAndMachines(context.Background(), nil, nil)
-		wantErr := "mcpLister is nil"
-
-		if got != false {
-			t.Fatalf("got %v, but wanted %v", got, false)
-		}
-		utilerror.AssertErrorMessage(t, err, wantErr)
-	})
-	t.Run("should return nil nodeLister error", func(t *testing.T) {
-		mcpLister := &fakeMcpLister{}
-		got, err := SameNumberOfNodesAndMachines(context.Background(), mcpLister, nil)
-		wantErr := "nodeLister is nil"
-
-		if got != false {
-			t.Fatalf("got %v, but wanted %v", got, false)
-		}
-		utilerror.AssertErrorMessage(t, err, wantErr)
-	})
-
-	t.Run("should propagate error from mcpLister.List()", func(t *testing.T) {
-		expectedErr := "some_error"
-		mcpLister := &fakeMcpLister{expectedErr: errors.New(expectedErr)}
-		nodeLister := &fakeNodeLister{}
-
-		got, err := SameNumberOfNodesAndMachines(context.Background(), mcpLister, nodeLister)
-		if got != false {
-			t.Fatalf("got %v, but wanted %v", got, false)
-		}
-		utilerror.AssertErrorMessage(t, err, expectedErr)
-	})
-	t.Run("should propagate error from TotalMachinesInTheMCPs()", func(t *testing.T) {
-		expectedErr := "ARO DNS config not found in MCP mcp_name"
-		badMachineConfigPools := []mcv1.MachineConfigPool{
-			{
-				ObjectMeta: metav1.ObjectMeta{Name: "mcp_name"},
-				Status: mcv1.MachineConfigPoolStatus{
-					ObservedGeneration: 0,
-					Configuration: mcv1.MachineConfigPoolStatusConfiguration{
-						Source: []corev1.ObjectReference{{Name: "non matching name"}},
-					},
-				},
-			},
-		}
-		machineConfigPoolList := &mcv1.MachineConfigPoolList{Items: badMachineConfigPools}
-		mcpLister := &fakeMcpLister{expected: machineConfigPoolList}
-		nodeLister := &fakeNodeLister{}
-
-		got, err := SameNumberOfNodesAndMachines(context.Background(), mcpLister, nodeLister)
-		if got != false {
-			t.Fatalf("got %v, but wanted %v", got, false)
-		}
-		utilerror.AssertErrorMessage(t, err, expectedErr)
-	})
-
-	t.Run("should propagate error from nodeLister", func(t *testing.T) {
-		expectedErr := "some_error"
-
-		machineConfigPools := []mcv1.MachineConfigPool{
-			{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:       "mcp_name",
-					Generation: 1,
-				},
-				Status: mcv1.MachineConfigPoolStatus{
-					MachineCount:        1,
-					UpdatedMachineCount: 1,
-					ReadyMachineCount:   1,
-					ObservedGeneration:  1,
-					Configuration: mcv1.MachineConfigPoolStatusConfiguration{
-						Source: []corev1.ObjectReference{{Name: "99-" + "mcp_name" + "-aro-dns"}},
-					},
-				},
-			},
-		}
-		machineConfigPoolList := &mcv1.MachineConfigPoolList{Items: machineConfigPools}
-		mcpLister := &fakeMcpLister{expected: machineConfigPoolList}
-		nodeLister := &fakeNodeLister{expectedErr: errors.New(expectedErr)}
-
-		got, err := SameNumberOfNodesAndMachines(context.Background(), mcpLister, nodeLister)
-		if got != false {
-			t.Fatalf("got %v, but wanted %v", got, false)
-		}
-		utilerror.AssertErrorMessage(t, err, expectedErr)
-	})
-
-	t.Run("should return true and no error", func(t *testing.T) {
-		expectedErr := ""
-		machineConfigPools := []mcv1.MachineConfigPool{
-			{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:       "mcp_name",
-					Generation: 1,
-				},
-				Status: mcv1.MachineConfigPoolStatus{
-					MachineCount:        1,
-					UpdatedMachineCount: 1,
-					ReadyMachineCount:   1,
-					ObservedGeneration:  1,
-					Configuration: mcv1.MachineConfigPoolStatusConfiguration{
-						Source: []corev1.ObjectReference{{Name: "99-" + "mcp_name" + "-aro-dns"}},
-					},
-				},
-			},
-		}
-		machineConfigPoolList := &mcv1.MachineConfigPoolList{Items: machineConfigPools}
-		mcpLister := &fakeMcpLister{expected: machineConfigPoolList}
-
-		nodes := &corev1.NodeList{Items: []corev1.Node{{}}}
-		nodeLister := &fakeNodeLister{expected: nodes}
-
-		got, err := SameNumberOfNodesAndMachines(context.Background(), mcpLister, nodeLister)
-		if got != true {
-			t.Fatalf("got %v, but wanted %v", got, true)
-		}
-		utilerror.AssertErrorMessage(t, err, expectedErr)
-	})
-
-	t.Run("should return false and error due to different number of total machines and nodes", func(t *testing.T) {
-		expectedErr := "cluster has 3 nodes but 1 under MCPs, not removing private DNS zone"
-		machineConfigPools := []mcv1.MachineConfigPool{
-			{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:       "mcp_name",
-					Generation: 1,
-				},
-				Status: mcv1.MachineConfigPoolStatus{
-					MachineCount:        1,
-					UpdatedMachineCount: 1,
-					ReadyMachineCount:   1,
-					ObservedGeneration:  1,
-					Configuration: mcv1.MachineConfigPoolStatusConfiguration{
-						Source: []corev1.ObjectReference{{Name: "99-" + "mcp_name" + "-aro-dns"}},
-					},
-				},
-			},
-		}
-		machineConfigPoolList := &mcv1.MachineConfigPoolList{Items: machineConfigPools}
-		mcpLister := &fakeMcpLister{expected: machineConfigPoolList}
-
-		nodes := &corev1.NodeList{Items: []corev1.Node{{}, {}, {}}}
-		nodeLister := &fakeNodeLister{expected: nodes}
-
-		got, err := SameNumberOfNodesAndMachines(context.Background(), mcpLister, nodeLister)
-		if got != false {
-			t.Fatalf("got %v, but wanted %v", got, false)
-		}
-		utilerror.AssertErrorMessage(t, err, expectedErr)
-	})
-}
diff --git a/test/e2e/ocp_cluster.go b/test/e2e/ocp_cluster.go
deleted file mode 100644
index b047495c7..000000000
--- a/test/e2e/ocp_cluster.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package e2e
-
-// Copyright (c) Microsoft Corporation.
-// Licensed under the Apache License 2.0.
-
-import (
-	"context"
-	"slices"
-	"strings"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-
-	configv1 "github.com/openshift/api/config/v1"
-	operatorv1 "github.com/openshift/api/operator/v1"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-)
-
-var _ = Describe("Cluster Operators", Label(smoke), func() {
-	It("should be all available", func(ctx context.Context) {
-		By("checking that all cluster operators are available")
-		cos, err := clients.ConfigClient.ConfigV1().ClusterOperators().List(ctx, metav1.ListOptions{})
-		Expect(err).NotTo(HaveOccurred())
-		for _, co := range cos.Items {
-			available := false
-			for _, condition := range co.Status.Conditions {
-				if condition.Type == configv1.OperatorAvailable {
-					Expect(condition.Status).To(Equal(configv1.ConditionTrue), "operator %s is not available", co.Name)
-					available = true
-					break
-				}
-			}
-			Expect(available).To(BeTrue(), "operator %s is not available", co.Name)
-		}
-	})
-})
-
-var _ = Describe("ARO Operator", Label(smoke), func() {
-	It("should meet all conditions", func(ctx context.Context) {
-		Eventually(func(g Gomega, ctx context.Context) {
-			co, err := clients.AROClusters.AroV1alpha1().Clusters().Get(ctx, "cluster", metav1.GetOptions{})
-			g.Expect(err).NotTo(HaveOccurred())
-
-			var skipConditions = []string{
-				"DefaultIngressCertificate", // This is not enabled in dev clusters and clusters with custom domains.
-			}
-
-			for _, condition := range co.Status.Conditions {
-				if strings.HasSuffix(condition.Type, "Progressing") || strings.HasSuffix(condition.Type, "Degraded") {
-					g.Expect(condition.Status).To(Equal(operatorv1.ConditionFalse))
-				} else if slices.Contains(skipConditions, condition.Type) {
-					continue
-				} else {
-					g.Expect(condition.Status).To(Equal(operatorv1.ConditionTrue))
-				}
-			}
-		}).WithContext(ctx).WithTimeout(DefaultEventuallyTimeout).Should(Succeed())
-	})
-})
-
-var _ = Describe("Node Ready", Label(smoke), func() {
-	It("should be ready", func(ctx context.Context) {
-		Eventually(func(g Gomega, ctx context.Context) {
-			nodes, err := clients.Kubernetes.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
-			g.Expect(err).NotTo(HaveOccurred())
-
-			for _, node := range nodes.Items {
-				for _, condition := range node.Status.Conditions {
-					switch condition.Type {
-					case corev1.NodeReady:
-						g.Expect(condition.Status).To(Equal(corev1.ConditionTrue))
-					case corev1.NodeMemoryPressure, corev1.NodeDiskPressure, corev1.NodePIDPressure, corev1.NodeNetworkUnavailable:
-						g.Expect(condition.Status).To(Equal(corev1.ConditionFalse))
-					}
-				}
-				g.Expect(node.Spec.Unschedulable).To(BeFalse())
-			}
-		}).WithContext(ctx).WithTimeout(DefaultEventuallyTimeout).Should(Succeed())
-	})
-})
diff --git a/test/e2e/setup.go b/test/e2e/setup.go
index 8a277cde4..2d7995d28 100644
--- a/test/e2e/setup.go
+++ b/test/e2e/setup.go
@@ -52,10 +52,6 @@ import (
 	"github.com/Azure/ARO-RP/test/util/kubeadminkubeconfig"
 )
 
-const (
-	smoke = "smoke"
-)
-
 var (
 	disallowedInFilenameRegex = regexp.MustCompile(`[<>:"/\\|?*\x00-\x1F]`)
 	DefaultEventuallyTimeout  = 5 * time.Minute