Repository: Azure/ARO-RP
Pull request #2608 (2022-12-22 10:45:36)

operator feature: check ingress certificate and cluster domain
### Which issue this PR addresses:

<!--
Please include a link to the ADO work item as well as any GitHub issues.

Usage: `Fixes #<GitHub issue number>`, or `Fixes (paste link of issue)`.
-->
Fixes [Improve detection of cases for ingress certificate](https://msazure.visualstudio.com/AzureRedHatOpenShift/_workitems/edit/15990932)

### What this PR does / why we need it:

<!--
Include a brief summary of what the PR is intended to accomplish and how the PR
does it. (2-3 sentences)
-->

pkg/operator/controllers/checkers/ingresscertificatechecker/checker.go contains code that currently detects if the default cert is specified in the ingress controller. But this check does not distinguish between the managed domain case and custom domain case.

This PR takes into account if we have a manage domain or not and report the error accordingly.
The [design doc](https://docs.google.com/document/d/13KgQOvUv3rV2vImzop74ouefhza7Owwk5K1bXoQLVR8/edit?usp=sharing) with the logic.

### Test plan for issue:

<!--
How did you test that this PR works?

- Are there unit tests?
- Are there integration/e2e tests?
- If it is not possible to write automated tests, explain why and document how
  to manually test and verify the feature.
-->
Unit Tests added.

### Is there any documentation that needs to be updated for this PR?

<!--
- If yes and the docs are in GitHub, include doc updates in the PR.
- If yes and the docs are not in GitHub (i.e. ADO wiki), include a link to the
  docs.
- If no, explain why (e.g. "tech debt cleanup, N/A").
-->

N/A

github-actions[bot]: Please rebase pull request.
gvanderpotte: LGTM - test coverage is good and does what we want

Patch:
diff --git a/.pipelines/deploy-dev-env.yml b/.pipelines/deploy-dev-env.yml
index 73bde6efd..7200c57ba 100644
--- a/.pipelines/deploy-dev-env.yml
+++ b/.pipelines/deploy-dev-env.yml
@@ -8,7 +8,7 @@ variables:
 jobs:
 - job: Deploy_new_shared_env
   pool:
-    name: ARO-Release
+    name: 1es-aro-ci-pool
 
   steps:
   - template: ./templates/template-checkout.yml
diff --git a/Dockerfile.proxy b/Dockerfile.proxy
index 8c878121f..9ad9696d7 100644
--- a/Dockerfile.proxy
+++ b/Dockerfile.proxy
@@ -1,7 +1,17 @@
+# Uses a multi-stage container build to build the proxy
+#
 ARG REGISTRY
+FROM ${REGISTRY}/ubi8/go-toolset:1.18.4 AS builder
+ENV GOOS=linux \
+    GOPATH=/go/
+WORKDIR ${GOPATH}/src/github.com/Azure/ARO-RP
+USER root
+COPY . ${GOPATH}/src/github.com/Azure/ARO-RP/
+RUN make proxy
+
 FROM ${REGISTRY}/ubi8/ubi-minimal
 RUN microdnf update && microdnf clean all
-COPY proxy /usr/local/bin
+COPY --from=builder /go/src/github.com/Azure/ARO-RP/proxy /usr/local/bin/
 ENTRYPOINT ["proxy"]
 EXPOSE 8443/tcp
 USER 1000
diff --git a/Makefile b/Makefile
index cd526782a..8f19e4387 100644
--- a/Makefile
+++ b/Makefile
@@ -98,7 +98,7 @@ image-autorest:
 image-fluentbit:
 	docker build --platform=linux/amd64 --network=host --build-arg VERSION=$(FLUENTBIT_VERSION) --build-arg MARINER_VERSION=$(MARINER_VERSION) --build-arg REGISTRY=$(REGISTRY) -f Dockerfile.fluentbit -t $(FLUENTBIT_IMAGE) .
 
-image-proxy: proxy
+image-proxy:
 	docker pull $(REGISTRY)/ubi8/ubi-minimal
 	docker build --platform=linux/amd64 --no-cache -f Dockerfile.proxy -t $(REGISTRY)/proxy:latest --build-arg REGISTRY=$(REGISTRY) .
 
diff --git a/hack/devtools/deploy-shared-env.sh b/hack/devtools/deploy-shared-env.sh
index 70ad7dbf1..0b3c10bf4 100644
--- a/hack/devtools/deploy-shared-env.sh
+++ b/hack/devtools/deploy-shared-env.sh
@@ -32,26 +32,6 @@ deploy_rp_dev() {
             "rpServicePrincipalId=$(az ad sp list --filter "appId eq '$AZURE_RP_CLIENT_ID'" --query '[].id' -o tsv)" >/dev/null
 }
 
-deploy_env_dev_ci() {
-    echo "########## Deploying env-development in RG $RESOURCEGROUP ##########"
-    az deployment group create \
-        -g "$RESOURCEGROUP" \
-        -n env-development \
-        --template-file pkg/deploy/assets/env-development.json \
-        --parameters \
-            "ciAzpToken=$AZPTOKEN" \
-            "ciCapacity=6" \
-            "ciPoolName=ARO-CI" \
-            "proxyCert=$(base64 -w0 <secrets/proxy.crt)" \
-            "proxyClientCert=$(base64 -w0 <secrets/proxy-client.crt)" \
-            "proxyDomainNameLabel=$(cut -d. -f2 <<<$PROXY_HOSTNAME)" \
-            "proxyImage=arointsvc.azurecr.io/proxy:latest" \
-            "proxyImageAuth=$(jq -r '.auths["arointsvc.azurecr.io"].auth' <<<$PULL_SECRET)" \
-            "proxyKey=$(base64 -w0 <secrets/proxy.key)" \
-            "vpnCACertificate=$(base64 -w0 <secrets/vpn-ca.crt)" \
-            "sshPublicKey=$(<secrets/proxy_id_rsa.pub)" >/dev/null
-}
-
 deploy_env_dev() {
     echo "########## Deploying env-development in RG $RESOURCEGROUP ##########"
     az deployment group create \
diff --git a/pkg/api/featureflags.go b/pkg/api/featureflags.go
index 25eb172b8..b274cf429 100644
--- a/pkg/api/featureflags.go
+++ b/pkg/api/featureflags.go
@@ -18,4 +18,8 @@ const (
 	// FeatureFlagUserDefinedRouting is the feature in the subscription that is used to indicate we need to
 	// provision a private cluster without an IP address
 	FeatureFlagUserDefinedRouting = "Microsoft.RedHatOpenShift/UserDefinedRouting"
+
+	// FeatureFlagCheckAccessTestToggle is used for safely testing the new check access
+	// API in production. The toggle will be removed once the testing has been completed.
+	FeatureFlagCheckAccessTestToggle = "Microsoft.RedHatOpenShift/CheckAccessTestToggle"
 )
diff --git a/pkg/deploy/assets/env-development.json b/pkg/deploy/assets/env-development.json
index 54ead1eda..7723defe1 100644
--- a/pkg/deploy/assets/env-development.json
+++ b/pkg/deploy/assets/env-development.json
@@ -2,18 +2,6 @@
     "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
     "contentVersion": "1.0.0.0",
     "parameters": {
-        "ciAzpToken": {
-            "type": "string",
-            "defaultValue": ""
-        },
-        "ciCapacity": {
-            "type": "int",
-            "defaultValue": 0
-        },
-        "ciPoolName": {
-            "type": "string",
-            "defaultValue": ""
-        },
         "proxyCert": {
             "type": "string"
         },
@@ -155,105 +143,6 @@
                 "[resourceId('Microsoft.Network/virtualNetworks', 'dev-vpn-vnet')]"
             ]
         },
-        {
-            "sku": {
-                "name": "Standard_D2s_v3",
-                "tier": "Standard",
-                "capacity": "[parameters('ciCapacity')]"
-            },
-            "properties": {
-                "upgradePolicy": {
-                    "mode": "Manual"
-                },
-                "virtualMachineProfile": {
-                    "osProfile": {
-                        "computerNamePrefix": "ci-",
-                        "adminUsername": "cloud-user",
-                        "linuxConfiguration": {
-                            "disablePasswordAuthentication": true,
-                            "ssh": {
-                                "publicKeys": [
-                                    {
-                                        "path": "/home/cloud-user/.ssh/authorized_keys",
-                                        "keyData": "[parameters('sshPublicKey')]"
-                                    }
-                                ]
-                            }
-                        }
-                    },
-                    "storageProfile": {
-                        "imageReference": {
-                            "publisher": "RedHat",
-                            "offer": "RHEL",
-                            "sku": "8-LVM",
-                            "version": "latest"
-                        },
-                        "osDisk": {
-                            "createOption": "FromImage",
-                            "diskSizeGB": 200,
-                            "managedDisk": {
-                                "storageAccountType": "Premium_LRS"
-                            }
-                        }
-                    },
-                    "networkProfile": {
-                        "networkInterfaceConfigurations": [
-                            {
-                                "name": "ci-vmss-nic",
-                                "properties": {
-                                    "primary": true,
-                                    "ipConfigurations": [
-                                        {
-                                            "name": "ci-vmss-ipconfig",
-                                            "properties": {
-                                                "subnet": {
-                                                    "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', 'dev-vnet', 'ToolingSubnet')]"
-                                                },
-                                                "primary": true,
-                                                "publicIPAddressConfiguration": {
-                                                    "name": "ci-vmss-pip",
-                                                    "properties": {
-                                                        "dnsSettings": {
-                                                            "domainNameLabel": "aro-ci"
-                                                        }
-                                                    }
-                                                }
-                                            }
-                                        }
-                                    ]
-                                }
-                            }
-                        ]
-                    },
-                    "extensionProfile": {
-                        "extensions": [
-                            {
-                                "name": "ci-vmss-cse",
-                                "properties": {
-                                    "publisher": "Microsoft.Azure.Extensions",
-                                    "type": "CustomScript",
-                                    "typeHandlerVersion": "2.0",
-                                    "autoUpgradeMinorVersion": true,
-                                    "settings": {},
-                                    "protectedSettings": {
-                                        "script": "[base64(concat(base64ToString('c2V0IC1lCgo='),'CIAZPTOKEN=''',parameters('ciAzpToken'),'''\n','CIPOOLNAME=''',parameters('ciPoolName'),'''\n','\n',base64ToString('IyBIYWNrIC0gd2FpdCBvbiBjcmVhdGUgYmVjYXVzZSB0aGUgV0FMaW51eEFnZW50IHNvbWV0aW1lcyBjb25mbGljdHMgd2l0aCB0aGUgeXVtIHVwZGF0ZSAteSBiZWxvdwpzbGVlcCA2MAoKZWNobyAicnVubmluZyBSSFVJIGZpeCIKeXVtIHVwZGF0ZSAteSAtLWRpc2FibGVyZXBvPScqJyAtLWVuYWJsZXJlcG89J3JodWktbWljcm9zb2Z0LWF6dXJlKicKCmZvciBhdHRlbXB0IGluIHsxLi41fTsgZG8KICB5dW0gLXkgdXBkYXRlICYmIGJyZWFrCiAgaWYgW1sgJHthdHRlbXB0fSAtbHQgNSBdXTsgdGhlbiBzbGVlcCAxMDsgZWxzZSBleGl0IDE7IGZpCmRvbmUKCkRFVklDRV9QQVJUSVRJT049JChwdnMgfCBncmVwICcvZGV2LycgfCBhd2sgJ3twcmludCAkMX0nIHwgZ3JlcCAtb1AgJ1thLXpdezN9WzAtOV0kJykKREVWSUNFPSQoZWNobyAkREVWSUNFX1BBUlRJVElPTiB8IGdyZXAgLW9QICdeW2Etel17M30nKQpQQVJUSVRJT049JChlY2hvICRERVZJQ0VfUEFSVElUSU9OIHwgZ3JlcCAtb1AgJ1swLTldJCcpCgojIEZpeCB0aGUgIkdQVCBQTUJSIHNpemUgbWlzbWF0Y2ggKDEzNDIxNzcyNyAhPSAyNjg0MzU0NTUpIgplY2hvICJ3IiB8IGZkaXNrIC9kZXYvJHtERVZJQ0V9CgojIFN0ZXBzIGZyb20gaHR0cHM6Ly9hY2Nlc3MucmVkaGF0LmNvbS9zb2x1dGlvbnMvNTgwODAwMQojIDEuIERlbGV0ZSB0aGUgTFZNIHBhcnRpdGlvbiAiZFxuMlxuIgojIDIuIFJlY3JlYXRlIHRoZSBwYXJ0aXRpb24gIm5cbjJcbiIKIyAzLiBBY2NlcHQgdGhlIGRlZmF1bHQgc3RhcnQgYW5kIGVuZCBzZWN0b3JzICgyIHggXG4pCiMgNC4gTFZNMl9tZW1iZXIgc2lnbmF0dXJlIHJlbWFpbnMgYnkgZGVmYXVsdAojIDUuIENoYW5nZSB0eXBlIHRvIExpbnV4IExWTSAidFxuMlxuMzFcbgojIDYuIFdyaXRlIG5ldyB0YWJsZSAid1xuIgoKZmRpc2sgL2Rldi8ke0RFVklDRX0gPDxFT0YKZAoke1BBUlRJVElPTn0Kbgoke1BBUlRJVElPTn0KCgp0CiR7UEFSVElUSU9OfQozMQp3CkVPRgoKcGFydHggLXUgL2Rldi8ke0RFVklDRX0KcHZyZXNpemUgL2Rldi8ke0RFVklDRV9QQVJUSVRJT059CgpsdmV4dGVuZCAtbCArNTAlRlJFRSAvZGV2L3Jvb3R2Zy9ob21lbHYKeGZzX2dyb3dmcyAvaG9tZQoKbHZleHRlbmQgLWwgKzUwJUZSRUUgL2Rldi9yb290dmcvdG1wbHYKeGZzX2dyb3dmcyAvdG1wCgpsdmV4dGVuZCAtbCArMTAwJUZSRUUgL2Rldi9yb290dmcvdmFybHYKeGZzX2dyb3dmcyAvdmFyCgpycG0gLS1pbXBvcnQgaHR0cHM6Ly9kbC5mZWRvcmFwcm9qZWN0Lm9yZy9wdWIvZXBlbC9SUE0tR1BHLUtFWS1FUEVMLTgKcnBtIC0taW1wb3J0IGh0dHBzOi8vcGFja2FnZXMubWljcm9zb2Z0LmNvbS9rZXlzL21pY3Jvc29mdC5hc2MKCnl1bSAteSBpbnN0YWxsIGh0dHBzOi8vZGwuZmVkb3JhcHJvamVjdC5vcmcvcHViL2VwZWwvZXBlbC1yZWxlYXNlLWxhdGVzdC04Lm5vYXJjaC5ycG0KCmNhdCA+L2V0Yy95dW0ucmVwb3MuZC9henVyZS5yZXBvIDw8J0VPRicKW2F6dXJlLWNsaV0KbmFtZT1henVyZS1jbGkKYmFzZXVybD1odHRwczovL3BhY2thZ2VzLm1pY3Jvc29mdC5jb20veXVtcmVwb3MvYXp1cmUtY2xpCmVuYWJsZWQ9eWVzCmdwZ2NoZWNrPXllcwpFT0YKCnl1bSAteSBpbnN0YWxsIGF6dXJlLWNsaSBwb2RtYW4gcG9kbWFuLWRvY2tlciBqcSBnY2MgZ3BnbWUtZGV2ZWwgbGliYXNzdWFuLWRldmVsIGdpdCBtYWtlIHRtcHdhdGNoIHB5dGhvbjMtZGV2ZWwgaHRvcCBnby10b29sc2V0LTEuMTcuMTItMS5tb2R1bGUrZWw4LjYuMCsxNjAxNCthMzcyYzAwYiBvcGVudnBuCgojIFN1cHByZXNzIGVtdWxhdGlvbiBvdXRwdXQgZm9yIHBvZG1hbiBpbnN0ZWFkIG9mIGRvY2tlciBmb3IgYXogYWNyIGNvbXBhdGFiaWxpdHkKbWtkaXIgLXAgL2V0Yy9jb250YWluZXJzLwp0b3VjaCAvZXRjL2NvbnRhaW5lcnMvbm9kb2NrZXIKClZTVFNfQUdFTlRfVkVSU0lPTj0yLjIwNi4xCm1rZGlyIC9ob21lL2Nsb3VkLXVzZXIvYWdlbnQKcHVzaGQgL2hvbWUvY2xvdWQtdXNlci9hZ2VudApjdXJsIC1zIGh0dHBzOi8vdnN0c2FnZW50cGFja2FnZS5henVyZWVkZ2UubmV0L2FnZW50LyR7VlNUU19BR0VOVF9WRVJTSU9OfS92c3RzLWFnZW50LWxpbnV4LXg2NC0ke1ZTVFNfQUdFTlRfVkVSU0lPTn0udGFyLmd6IHwgdGFyIC14egpjaG93biAtUiBjbG91ZC11c2VyOmNsb3VkLXVzZXIgLgoKLi9iaW4vaW5zdGFsbGRlcGVuZGVuY2llcy5zaApzdWRvIC11IGNsb3VkLXVzZXIgLi9jb25maWcuc2ggLS11bmF0dGVuZGVkIC0tdXJsIGh0dHBzOi8vZGV2LmF6dXJlLmNvbS9tc2F6dXJlIC0tYXV0aCBwYXQgLS10b2tlbiAiJENJQVpQVE9LRU4iIC0tcG9vbCAiJENJUE9PTE5BTUUiIC0tYWdlbnQgIkFSTy1SSEVMLSRIT1NUTkFNRSIgLS1yZXBsYWNlCi4vc3ZjLnNoIGluc3RhbGwgY2xvdWQtdXNlcgpwb3BkCgpjYXQgPi9ob21lL2Nsb3VkLXVzZXIvYWdlbnQvLnBhdGggPDwnRU9GJwovdXNyL2xvY2FsL2JpbjovdXNyL2JpbjovdXNyL2xvY2FsL3NiaW46L3Vzci9zYmluOi9ob21lL2Nsb3VkLXVzZXIvLmxvY2FsL2JpbjovaG9tZS9jbG91ZC11c2VyL2JpbgpFT0YKCiMgU2V0IHRoZSBhZ2VudCdzICJTeXN0ZW0gY2FwYWJpbGl0aWVzIiBmb3IgdGVzdHMgKGdvLTEuMTcgYW5kIEdPTEFOR19GSVBTKSBpbiB0aGUgYWdlbnQncyAuZW52IGZpbGUKIyBhbmQgYWRkIGEgSEFDSyBmb3IgWERHX1JVTlRJTUVfRElSOiBodHRwczovL2dpdGh1Yi5jb20vY29udGFpbmVycy9wb2RtYW4vaXNzdWVzLzQyNwpjYXQgPi9ob21lL2Nsb3VkLXVzZXIvYWdlbnQvLmVudiA8PCdFT0YnCmdvLTEuMTc9dHJ1ZQpHT0xBTkdfRklQUz0xClhER19SVU5USU1FX0RJUj0vcnVuL3VzZXIvMTAwMApFT0YKCmNhdCA+L2V0Yy9jcm9uLndlZWtseS95dW11cGRhdGUgPDwnRU9GJwojIS9iaW4vYmFzaAoKeXVtIHVwZGF0ZSAteQpFT0YKY2htb2QgK3ggL2V0Yy9jcm9uLndlZWtseS95dW11cGRhdGUKCmNhdCA+L2V0Yy9jcm9uLmhvdXJseS90bXB3YXRjaCA8PCdFT0YnCiMhL2Jpbi9iYXNoCgpleGVjIC9zYmluL3RtcHdhdGNoIDI0aCAvdG1wCkVPRgpjaG1vZCAreCAvZXRjL2Nyb24uaG91cmx5L3RtcHdhdGNoCgojIEhBQ0sgLSBwb2RtYW4gZG9lc24ndCBhbHdheXMgdGVybWluYXRlIG9yIGNsZWFuIHVwIGl0J3MgcGF1c2UucGlkIGZpbGUgY2F1c2luZwojICdjYW5ub3QgcmVleGVjIGVycm9ycycgc28gYXR0ZW1wdCB0byBjbGVhbiBpdCB1cCBldmVyeSBtaW51dGUgdG8ga2VlcCBwaXBlbGluZXMgcnVubmluZwojIHNtb290aGx5CmNhdCA+L3Vzci9sb2NhbC9iaW4vZml4LXBvZG1hbi1wYXVzZS5zaCA8PCdFT0YnCiMhL2Jpbi9iYXNoCgpQQVVTRV9GSUxFPScvdG1wL3BvZG1hbi1ydW4tMTAwMC9saWJwb2QvdG1wL3BhdXNlLnBpZCcKCmlmIFsgLWYgIiR7UEFVU0VfRklMRX0iIF07IHRoZW4KCVBJRD0kKGNhdCAke1BBVVNFX0ZJTEV9KQoJaWYgISBwcyAtcCAkUElEID4gL2Rldi9udWxsOyB0aGVuCgkJcm0gJFBBVVNFX0ZJTEUKCWZpCmZpCkVPRgpjaG1vZCAreCAvdXNyL2xvY2FsL2Jpbi9maXgtcG9kbWFuLXBhdXNlLnNoCgojIEhBQ0sgLSAvdG1wIHdpbGwgZmlsbCB1cCBjYXVzaW5nIGJ1aWxkIGZhaWx1cmVzCiMgZGVsZXRlIGFueXRoaW5nIG5vdCBhY2Nlc3NlZCB3aXRoaW4gMiBkYXlzCmNhdCA+L3Vzci9sb2NhbC9iaW4vY2xlYW4tdG1wLnNoIDw8J0VPRicKIyEvYmluL2Jhc2gKCmZpbmQgL3RtcCAtdHlwZSBmIFwoICEgLXVzZXIgcm9vdCBcKSAtYXRpbWUgKzIgLWRlbGV0ZQoKRU9GCmNobW9kICt4IC91c3IvbG9jYWwvYmluL2NsZWFuLXRtcC5zaAoKZWNobyAiMCAwICovMSAqICogL3Vzci9sb2NhbC9iaW4vY2xlYW4tdG1wLnNoIiA+PiBjcm9uCmVjaG8gIiogKiAqICogKiAvdXNyL2xvY2FsL2Jpbi9maXgtcG9kbWFuLXBhdXNlLnNoIiA+PiBjcm9uCgojIEhBQ0sgLSBodHRwczovL2dpdGh1Yi5jb20vY29udGFpbmVycy9wb2RtYW4vaXNzdWVzLzkwMDIKZWNobyAiQHJlYm9vdCBsb2dpbmN0bCBlbmFibGUtbGluZ2VyIGNsb3VkLXVzZXIiID4+IGNyb24KCmNyb250YWIgY3JvbgpybSBjcm9uCgooc2xlZXAgMzA7IHJlYm9vdCkgJgo=')))]"
-                                    }
-                                }
-                            }
-                        ]
-                    }
-                },
-                "overprovision": false
-            },
-            "name": "ci-vmss",
-            "type": "Microsoft.Compute/virtualMachineScaleSets",
-            "location": "[resourceGroup().location]",
-            "condition": "[greater(parameters('ciCapacity'), 0)]",
-            "apiVersion": "2020-12-01",
-            "dependsOn": [
-                "[resourceId('Microsoft.Network/virtualNetworks', 'dev-vnet')]"
-            ]
-        },
         {
             "name": "[concat(take(resourceGroup().name,10), '-dev-sharedKV')]",
             "type": "Microsoft.KeyVault/vaults",
@@ -357,7 +246,7 @@
                         "imageReference": {
                             "publisher": "RedHat",
                             "offer": "RHEL",
-                            "sku": "7-LVM",
+                            "sku": "8-LVM",
                             "version": "latest"
                         },
                         "osDisk": {
@@ -407,7 +296,39 @@
                                     "autoUpgradeMinorVersion": true,
                                     "settings": {},
                                     "protectedSettings": {
-                                        "script": "[base64(concat(base64ToString('c2V0IC1leAoK'),'PROXYIMAGE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('proxyImage')),''')\n','PROXYIMAGEAUTH=$(base64 -d \u003c\u003c\u003c''',base64(parameters('proxyImageAuth')),''')\n','PROXYCERT=''',parameters('proxyCert'),'''\n','PROXYCLIENTCERT=''',parameters('proxyClientCert'),'''\n','PROXYKEY=''',parameters('proxyKey'),'''\n','\n',base64ToString('ZWNobyAicnVubmluZyBSSFVJIGZpeCIKeXVtIHVwZGF0ZSAteSAtLWRpc2FibGVyZXBvPScqJyAtLWVuYWJsZXJlcG89J3JodWktbWljcm9zb2Z0LWF6dXJlKicKCnl1bSAteSAteCBXQUxpbnV4QWdlbnQgLXggV0FMaW51eEFnZW50LXVkZXYgdXBkYXRlCnl1bSAteSBpbnN0YWxsIGRvY2tlcgoKZmlyZXdhbGwtY21kIC0tYWRkLXBvcnQ9NDQzL3RjcCAtLXBlcm1hbmVudAoKbWtkaXIgL3Jvb3QvLmRvY2tlcgpjYXQgPi9yb290Ly5kb2NrZXIvY29uZmlnLmpzb24gPDxFT0YKewoJImF1dGhzIjogewoJCSIke1BST1hZSU1BR0UlJS8qfSI6IHsKCQkJImF1dGgiOiAiJFBST1hZSU1BR0VBVVRIIgoJCX0KCX0KfQpFT0YKc3lzdGVtY3RsIHN0YXJ0IGRvY2tlci5zZXJ2aWNlCmRvY2tlciBwdWxsICIkUFJPWFlJTUFHRSIKCm1rZGlyIC9ldGMvcHJveHkKYmFzZTY0IC1kIDw8PCIkUFJPWFlDRVJUIiA+L2V0Yy9wcm94eS9wcm94eS5jcnQKYmFzZTY0IC1kIDw8PCIkUFJPWFlLRVkiID4vZXRjL3Byb3h5L3Byb3h5LmtleQpiYXNlNjQgLWQgPDw8IiRQUk9YWUNMSUVOVENFUlQiID4vZXRjL3Byb3h5L3Byb3h5LWNsaWVudC5jcnQKY2hvd24gLVIgMTAwMDoxMDAwIC9ldGMvcHJveHkKY2htb2QgMDYwMCAvZXRjL3Byb3h5L3Byb3h5LmtleQoKY2F0ID4vZXRjL3N5c2NvbmZpZy9wcm94eSA8PEVPRgpQUk9YWV9JTUFHRT0nJFBST1hZSU1BR0UnCkVPRgoKY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL3Byb3h5LnNlcnZpY2UgPDwnRU9GJwpbVW5pdF0KQWZ0ZXI9ZG9ja2VyLnNlcnZpY2UKUmVxdWlyZXM9ZG9ja2VyLnNlcnZpY2UKCltTZXJ2aWNlXQpFbnZpcm9ubWVudEZpbGU9L2V0Yy9zeXNjb25maWcvcHJveHkKRXhlY1N0YXJ0UHJlPS0vdXNyL2Jpbi9kb2NrZXIgcm0gLWYgJW4KRXhlY1N0YXJ0PS91c3IvYmluL2RvY2tlciBydW4gLS1ybSAtLW5hbWUgJW4gLXAgNDQzOjg0NDMgLXYgL2V0Yy9wcm94eTovc2VjcmV0cyAkUFJPWFlfSU1BR0UKRXhlY1N0b3A9L3Vzci9iaW4vZG9ja2VyIHN0b3AgJW4KUmVzdGFydD1hbHdheXMKUmVzdGFydFNlYz0xClN0YXJ0TGltaXRJbnRlcnZhbD0wCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQKRU9GCgpzeXN0ZW1jdGwgZW5hYmxlIHByb3h5LnNlcnZpY2UKCmNhdCA+L2V0Yy9jcm9uLndlZWtseS9wdWxsLWltYWdlIDw8J0VPRicKIyEvYmluL2Jhc2gKCmRvY2tlciBwdWxsICRQUk9YWUlNQUdFCnN5c3RlbWN0bCByZXN0YXJ0IHByb3h5LnNlcnZpY2UKRU9GCmNobW9kICt4IC9ldGMvY3Jvbi53ZWVrbHkvcHVsbC1pbWFnZQoKY2F0ID4vZXRjL2Nyb24ud2Vla2x5L3l1bXVwZGF0ZSA8PCdFT0YnCiMhL2Jpbi9iYXNoCgp5dW0gdXBkYXRlIC15CkVPRgpjaG1vZCAreCAvZXRjL2Nyb24ud2Vla2x5L3l1bXVwZGF0ZQoKKHNsZWVwIDMwOyByZWJvb3QpICYK')))]"
+                                        "script": "[base64(concat(base64ToString('c2V0IC1leAoK'),'PROXYIMAGE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('proxyImage')),''')\n','PROXYIMAGEAUTH=$(base64 -d \u003c\u003c\u003c''',base64(parameters('proxyImageAuth')),''')\n','PROXYCERT=''',parameters('proxyCert'),'''\n','PROXYCLIENTCERT=''',parameters('proxyClientCert'),'''\n','PROXYKEY=''',parameters('proxyKey'),'''\n','\n',base64ToString('ZWNobyAicnVubmluZyBSSFVJIGZpeCIKeXVtIHVwZGF0ZSAteSAtLWRpc2FibGVyZXBvPScqJyAtLWVuYWJsZXJlcG89J3JodWktbWljcm9zb2Z0LWF6dXJlKicKCnl1bSAteSAteCBXQUxpbnV4QWdlbnQgLXggV0FMaW51eEFnZW50LXVkZXYgdXBkYXRlCnl1bSAteSBpbnN0YWxsIHBvZG1hbi1kb2NrZXIKCmZpcmV3YWxsLWNtZCAtLWFkZC1wb3J0PTQ0My90Y3AgLS1wZXJtYW5lbnQKCm1rZGlyIC9yb290Ly5kb2NrZXIKY2F0ID4vcm9vdC8uZG9ja2VyL2NvbmZpZy5qc29uIDw8RU9GCnsKCSJhdXRocyI6IHsKCQkiJHtQUk9YWUlNQUdFJSUvKn0iOiB7CgkJCSJhdXRoIjogIiRQUk9YWUlNQUdFQVVUSCIKCQl9Cgl9Cn0KRU9GCgpta2RpciAtcCAvZXRjL2NvbnRhaW5lcnMvCnRvdWNoIC9ldGMvY29udGFpbmVycy9ub2RvY2tlcgoKZG9ja2VyIHB1bGwgIiRQUk9YWUlNQUdFIgoKbWtkaXIgL2V0Yy9wcm94eQpiYXNlNjQgLWQgPDw8IiRQUk9YWUNFUlQiID4vZXRjL3Byb3h5L3Byb3h5LmNydApiYXNlNjQgLWQgPDw8IiRQUk9YWUtFWSIgPi9ldGMvcHJveHkvcHJveHkua2V5CmJhc2U2NCAtZCA8PDwiJFBST1hZQ0xJRU5UQ0VSVCIgPi9ldGMvcHJveHkvcHJveHktY2xpZW50LmNydApjaG93biAtUiAxMDAwOjEwMDAgL2V0Yy9wcm94eQpjaG1vZCAwNjAwIC9ldGMvcHJveHkvcHJveHkua2V5CgpjYXQgPi9ldGMvc3lzY29uZmlnL3Byb3h5IDw8RU9GClBST1hZX0lNQUdFPSckUFJPWFlJTUFHRScKRU9GCgpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vcHJveHkuc2VydmljZSA8PCdFT0YnCltVbml0XQpBZnRlcj1uZXR3b3JrLW9ubGluZS50YXJnZXQKV2FudHM9bmV0d29yay1vbmxpbmUudGFyZ2V0CgpbU2VydmljZV0KRW52aXJvbm1lbnRGaWxlPS9ldGMvc3lzY29uZmlnL3Byb3h5CkV4ZWNTdGFydFByZT0tL3Vzci9iaW4vZG9ja2VyIHJtIC1mICVuCkV4ZWNTdGFydD0vdXNyL2Jpbi9kb2NrZXIgcnVuIC0tcm0gLS1uYW1lICVuIC1wIDQ0Mzo4NDQzIC12IC9ldGMvcHJveHk6L3NlY3JldHMgJFBST1hZX0lNQUdFCkV4ZWNTdG9wPS91c3IvYmluL2RvY2tlciBzdG9wICVuClJlc3RhcnQ9YWx3YXlzClJlc3RhcnRTZWM9MQpTdGFydExpbWl0SW50ZXJ2YWw9MAoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIudGFyZ2V0CkVPRgoKc3lzdGVtY3RsIGVuYWJsZSBwcm94eS5zZXJ2aWNlCgpjYXQgPi9ldGMvY3Jvbi53ZWVrbHkvcHVsbC1pbWFnZSA8PCdFT0YnCiMhL2Jpbi9iYXNoCgpkb2NrZXIgcHVsbCAkUFJPWFlJTUFHRQpzeXN0ZW1jdGwgcmVzdGFydCBwcm94eS5zZXJ2aWNlCkVPRgpjaG1vZCAreCAvZXRjL2Nyb24ud2Vla2x5L3B1bGwtaW1hZ2UKCmNhdCA+L2V0Yy9jcm9uLndlZWtseS95dW11cGRhdGUgPDwnRU9GJwojIS9iaW4vYmFzaAoKeXVtIHVwZGF0ZSAteQpFT0YKY2htb2QgK3ggL2V0Yy9jcm9uLndlZWtseS95dW11cGRhdGUKCigKCXNsZWVwIDMwCglyZWJvb3QKKSAmCg==')))]"
+                                    },
+                                    "provisionAfterExtensions": [
+                                        "Microsoft.Azure.Monitor.AzureMonitorLinuxAgent",
+                                        "Microsoft.Azure.Security.Monitoring.AzureSecurityLinuxAgent"
+                                    ]
+                                }
+                            },
+                            {
+                                "name": "Microsoft.Azure.Monitor.AzureMonitorLinuxAgent",
+                                "properties": {
+                                    "publisher": "Microsoft.Azure.Monitor",
+                                    "type": "AzureMonitorLinuxAgent",
+                                    "typeHandlerVersion": "1.0",
+                                    "autoUpgradeMinorVersion": true,
+                                    "enableAutomaticUpgrade": true,
+                                    "settings": {
+                                        "GCS_AUTO_CONFIG": true
+                                    }
+                                }
+                            },
+                            {
+                                "name": "Microsoft.Azure.Security.Monitoring.AzureSecurityLinuxAgent",
+                                "properties": {
+                                    "publisher": "Microsoft.Azure.Security.Monitoring",
+                                    "type": "AzureSecurityLinuxAgent",
+                                    "typeHandlerVersion": "2.0",
+                                    "autoUpgradeMinorVersion": true,
+                                    "enableAutomaticUpgrade": true,
+                                    "settings": {
+                                        "enableAutoConfig": true,
+                                        "enableGenevaUpload": true,
+                                        "reportSuccessOnUnsupportedDistro": true
                                     }
                                 }
                             }
diff --git a/pkg/deploy/assets/rp-production.json b/pkg/deploy/assets/rp-production.json
index 69e630caa..43588b2a0 100644
--- a/pkg/deploy/assets/rp-production.json
+++ b/pkg/deploy/assets/rp-production.json
@@ -8,9 +8,7 @@
             },
             {
                 "id": "[resourceId(parameters('gatewayResourceGroupName'), 'Microsoft.Network/virtualNetworks/subnets', 'gateway-vnet', 'gateway-subnet')]"
-            }
-        ],
-        "rpCosmoDbVirtualNetworkRulesVmDeploy": [
+            },
             {
                 "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', 'aks-net', 'ClusterSubnet-001')]"
             }
@@ -750,7 +748,7 @@
                 "databaseAccountOfferType": "Standard",
                 "ipRules": "[if(parameters('disableCosmosDBFirewall'), createArray(), concat(parameters('ipRules'),createArray(createObject('ipAddressOrRange', '104.42.195.92'),createObject('ipAddressOrRange','40.76.54.131'),createObject('ipAddressOrRange','52.176.6.30'),createObject('ipAddressOrRange','52.169.50.45'),createObject('ipAddressOrRange','52.187.184.26'))))]",
                 "isVirtualNetworkFilterEnabled": "[not(parameters('disableCosmosDBFirewall'))]",
-                "virtualNetworkRules": "[if(parameters('disableCosmosDBFirewall'), createArray(), if(startsWith(toLower(replace(resourceGroup().location, ' ', '')), 'usgov'), concat(variables('rpCosmoDbVirtualNetworkRules'), variables('rpCosmoDbVirtualNetworkRulesVmDeploy')), variables('rpCosmoDbVirtualNetworkRules')))]",
+                "virtualNetworkRules": "[if(parameters('disableCosmosDBFirewall'), createArray(), variables('rpCosmoDbVirtualNetworkRules'))]",
                 "disableKeyBasedMetadataWriteAccess": true,
                 "backupPolicy": {
                     "periodicModeProperties": {
diff --git a/pkg/deploy/generator/resources_dev.go b/pkg/deploy/generator/resources_dev.go
index 3d29811ab..dbd1fd960 100644
--- a/pkg/deploy/generator/resources_dev.go
+++ b/pkg/deploy/generator/resources_dev.go
@@ -75,7 +75,7 @@ func (g *generator) devProxyVMSS() *arm.Resource {
 						ImageReference: &mgmtcompute.ImageReference{
 							Publisher: to.StringPtr("RedHat"),
 							Offer:     to.StringPtr("RHEL"),
-							Sku:       to.StringPtr("7-LVM"),
+							Sku:       to.StringPtr("8-LVM"),
 							Version:   to.StringPtr("latest"),
 						},
 						OsDisk: &mgmtcompute.VirtualMachineScaleSetOSDisk{
@@ -119,9 +119,13 @@ func (g *generator) devProxyVMSS() *arm.Resource {
 							{
 								Name: to.StringPtr("dev-proxy-vmss-cse"),
 								VirtualMachineScaleSetExtensionProperties: &mgmtcompute.VirtualMachineScaleSetExtensionProperties{
-									Publisher:               to.StringPtr("Microsoft.Azure.Extensions"),
-									Type:                    to.StringPtr("CustomScript"),
-									TypeHandlerVersion:      to.StringPtr("2.0"),
+									Publisher:          to.StringPtr("Microsoft.Azure.Extensions"),
+									Type:               to.StringPtr("CustomScript"),
+									TypeHandlerVersion: to.StringPtr("2.0"),
+									ProvisionAfterExtensions: &[]string{
+										"Microsoft.Azure.Monitor.AzureMonitorLinuxAgent",
+										"Microsoft.Azure.Security.Monitoring.AzureSecurityLinuxAgent",
+									},
 									AutoUpgradeMinorVersion: to.BoolPtr(true),
 									Settings:                map[string]interface{}{},
 									ProtectedSettings: map[string]interface{}{
@@ -129,6 +133,34 @@ func (g *generator) devProxyVMSS() *arm.Resource {
 									},
 								},
 							},
+							{
+								Name: to.StringPtr("Microsoft.Azure.Monitor.AzureMonitorLinuxAgent"),
+								VirtualMachineScaleSetExtensionProperties: &mgmtcompute.VirtualMachineScaleSetExtensionProperties{
+									Publisher:               to.StringPtr("Microsoft.Azure.Monitor"),
+									Type:                    to.StringPtr("AzureMonitorLinuxAgent"),
+									TypeHandlerVersion:      to.StringPtr("1.0"),
+									AutoUpgradeMinorVersion: to.BoolPtr(true),
+									EnableAutomaticUpgrade:  to.BoolPtr(true),
+									Settings: map[string]interface{}{
+										"GCS_AUTO_CONFIG": true,
+									},
+								},
+							},
+							{
+								Name: to.StringPtr("Microsoft.Azure.Security.Monitoring.AzureSecurityLinuxAgent"),
+								VirtualMachineScaleSetExtensionProperties: &mgmtcompute.VirtualMachineScaleSetExtensionProperties{
+									Publisher:               to.StringPtr("Microsoft.Azure.Security.Monitoring"),
+									Type:                    to.StringPtr("AzureSecurityLinuxAgent"),
+									TypeHandlerVersion:      to.StringPtr("2.0"),
+									AutoUpgradeMinorVersion: to.BoolPtr(true),
+									EnableAutomaticUpgrade:  to.BoolPtr(true),
+									Settings: map[string]interface{}{
+										"enableGenevaUpload":               true,
+										"enableAutoConfig":                 true,
+										"reportSuccessOnUnsupportedDistro": true,
+									},
+								},
+							},
 						},
 					},
 				},
@@ -235,130 +267,6 @@ func (g *generator) devVPN() *arm.Resource {
 	}
 }
 
-func (g *generator) devCIPool() *arm.Resource {
-	parts := []string{
-		fmt.Sprintf("base64ToString('%s')", base64.StdEncoding.EncodeToString([]byte("set -e\n\n"))),
-	}
-
-	for _, variable := range []string{
-		"ciAzpToken",
-		"ciPoolName"} {
-		parts = append(parts,
-			fmt.Sprintf("'%s='''", strings.ToUpper(variable)),
-			fmt.Sprintf("parameters('%s')", variable),
-			"'''\n'",
-		)
-	}
-
-	trailer := base64.StdEncoding.EncodeToString(scriptDevCIPool)
-
-	parts = append(parts, "'\n'", fmt.Sprintf("base64ToString('%s')", trailer))
-
-	script := fmt.Sprintf("[base64(concat(%s))]", strings.Join(parts, ","))
-
-	return &arm.Resource{
-		Resource: &mgmtcompute.VirtualMachineScaleSet{
-			Sku: &mgmtcompute.Sku{
-				Name:     to.StringPtr(string(mgmtcompute.VirtualMachineSizeTypesStandardD2sV3)),
-				Tier:     to.StringPtr("Standard"),
-				Capacity: to.Int64Ptr(1337),
-			},
-			VirtualMachineScaleSetProperties: &mgmtcompute.VirtualMachineScaleSetProperties{
-				UpgradePolicy: &mgmtcompute.UpgradePolicy{
-					Mode: mgmtcompute.UpgradeModeManual,
-				},
-				VirtualMachineProfile: &mgmtcompute.VirtualMachineScaleSetVMProfile{
-					OsProfile: &mgmtcompute.VirtualMachineScaleSetOSProfile{
-						ComputerNamePrefix: to.StringPtr("ci-"),
-						AdminUsername:      to.StringPtr("cloud-user"),
-						LinuxConfiguration: &mgmtcompute.LinuxConfiguration{
-							DisablePasswordAuthentication: to.BoolPtr(true),
-							SSH: &mgmtcompute.SSHConfiguration{
-								PublicKeys: &[]mgmtcompute.SSHPublicKey{
-									{
-										Path:    to.StringPtr("/home/cloud-user/.ssh/authorized_keys"),
-										KeyData: to.StringPtr("[parameters('sshPublicKey')]"),
-									},
-								},
-							},
-						},
-					},
-					StorageProfile: &mgmtcompute.VirtualMachineScaleSetStorageProfile{
-						ImageReference: &mgmtcompute.ImageReference{
-							Publisher: to.StringPtr("RedHat"),
-							Offer:     to.StringPtr("RHEL"),
-							Sku:       to.StringPtr("8-LVM"),
-							Version:   to.StringPtr("latest"),
-						},
-						OsDisk: &mgmtcompute.VirtualMachineScaleSetOSDisk{
-							CreateOption: mgmtcompute.DiskCreateOptionTypesFromImage,
-							ManagedDisk: &mgmtcompute.VirtualMachineScaleSetManagedDiskParameters{
-								StorageAccountType: mgmtcompute.StorageAccountTypesPremiumLRS,
-							},
-							DiskSizeGB: to.Int32Ptr(200),
-						},
-					},
-					NetworkProfile: &mgmtcompute.VirtualMachineScaleSetNetworkProfile{
-						NetworkInterfaceConfigurations: &[]mgmtcompute.VirtualMachineScaleSetNetworkConfiguration{
-							{
-								Name: to.StringPtr("ci-vmss-nic"),
-								VirtualMachineScaleSetNetworkConfigurationProperties: &mgmtcompute.VirtualMachineScaleSetNetworkConfigurationProperties{
-									Primary: to.BoolPtr(true),
-									IPConfigurations: &[]mgmtcompute.VirtualMachineScaleSetIPConfiguration{
-										{
-											Name: to.StringPtr("ci-vmss-ipconfig"),
-											VirtualMachineScaleSetIPConfigurationProperties: &mgmtcompute.VirtualMachineScaleSetIPConfigurationProperties{
-												Subnet: &mgmtcompute.APIEntityReference{
-													ID: to.StringPtr("[resourceId('Microsoft.Network/virtualNetworks/subnets', 'dev-vnet', 'ToolingSubnet')]"),
-												},
-												Primary: to.BoolPtr(true),
-												PublicIPAddressConfiguration: &mgmtcompute.VirtualMachineScaleSetPublicIPAddressConfiguration{
-													Name: to.StringPtr("ci-vmss-pip"),
-													VirtualMachineScaleSetPublicIPAddressConfigurationProperties: &mgmtcompute.VirtualMachineScaleSetPublicIPAddressConfigurationProperties{
-														DNSSettings: &mgmtcompute.VirtualMachineScaleSetPublicIPAddressConfigurationDNSSettings{
-															DomainNameLabel: to.StringPtr("aro-ci"),
-														},
-													},
-												},
-											},
-										},
-									},
-								},
-							},
-						},
-					},
-					ExtensionProfile: &mgmtcompute.VirtualMachineScaleSetExtensionProfile{
-						Extensions: &[]mgmtcompute.VirtualMachineScaleSetExtension{
-							{
-								Name: to.StringPtr("ci-vmss-cse"),
-								VirtualMachineScaleSetExtensionProperties: &mgmtcompute.VirtualMachineScaleSetExtensionProperties{
-									Publisher:               to.StringPtr("Microsoft.Azure.Extensions"),
-									Type:                    to.StringPtr("CustomScript"),
-									TypeHandlerVersion:      to.StringPtr("2.0"),
-									AutoUpgradeMinorVersion: to.BoolPtr(true),
-									Settings:                map[string]interface{}{},
-									ProtectedSettings: map[string]interface{}{
-										"script": script,
-									},
-								},
-							},
-						},
-					},
-				},
-				Overprovision: to.BoolPtr(false),
-			},
-			Name:     to.StringPtr("ci-vmss"),
-			Type:     to.StringPtr("Microsoft.Compute/virtualMachineScaleSets"),
-			Location: to.StringPtr("[resourceGroup().location]"),
-		},
-		APIVersion: azureclient.APIVersion("Microsoft.Compute"),
-		Condition:  "[greater(parameters('ciCapacity'), 0)]", // TODO(mj): Refactor g.conditionStanza for better usage
-		DependsOn: []string{
-			"[resourceId('Microsoft.Network/virtualNetworks', 'dev-vnet')]",
-		},
-	}
-}
-
 const (
 	sharedKeyVaultName          = "concat(take(resourceGroup().name,10), '" + SharedKeyVaultNameSuffix + "')"
 	sharedDiskEncryptionSetName = "concat(resourceGroup().name, '" + SharedDiskEncryptionSetNameSuffix + "')"
diff --git a/pkg/deploy/generator/scripts.go b/pkg/deploy/generator/scripts.go
index 117ad6738..a60f9fefb 100644
--- a/pkg/deploy/generator/scripts.go
+++ b/pkg/deploy/generator/scripts.go
@@ -10,9 +10,6 @@ import (
 //go:embed scripts/devProxyVMSS.sh
 var scriptDevProxyVMSS []byte
 
-//go:embed scripts/devCIPool.sh
-var scriptDevCIPool []byte
-
 //go:embed scripts/gatewayVMSS.sh
 var scriptGatewayVMSS []byte
 
diff --git a/pkg/deploy/generator/scripts/devCIPool.sh b/pkg/deploy/generator/scripts/devCIPool.sh
deleted file mode 100644
index 4dbb94b66..000000000
--- a/pkg/deploy/generator/scripts/devCIPool.sh
+++ /dev/null
@@ -1,144 +0,0 @@
-# Hack - wait on create because the WALinuxAgent sometimes conflicts with the yum update -y below
-sleep 60
-
-echo "running RHUI fix"
-yum update -y --disablerepo='*' --enablerepo='rhui-microsoft-azure*'
-
-for attempt in {1..5}; do
-  yum -y update && break
-  if [[ ${attempt} -lt 5 ]]; then sleep 10; else exit 1; fi
-done
-
-DEVICE_PARTITION=$(pvs | grep '/dev/' | awk '{print $1}' | grep -oP '[a-z]{3}[0-9]$')
-DEVICE=$(echo $DEVICE_PARTITION | grep -oP '^[a-z]{3}')
-PARTITION=$(echo $DEVICE_PARTITION | grep -oP '[0-9]$')
-
-# Fix the "GPT PMBR size mismatch (134217727 != 268435455)"
-echo "w" | fdisk /dev/${DEVICE}
-
-# Steps from https://access.redhat.com/solutions/5808001
-# 1. Delete the LVM partition "d\n2\n"
-# 2. Recreate the partition "n\n2\n"
-# 3. Accept the default start and end sectors (2 x \n)
-# 4. LVM2_member signature remains by default
-# 5. Change type to Linux LVM "t\n2\n31\n
-# 6. Write new table "w\n"
-
-fdisk /dev/${DEVICE} <<EOF
-d
-${PARTITION}
-n
-${PARTITION}
-
-
-t
-${PARTITION}
-31
-w
-EOF
-
-partx -u /dev/${DEVICE}
-pvresize /dev/${DEVICE_PARTITION}
-
-lvextend -l +50%FREE /dev/rootvg/homelv
-xfs_growfs /home
-
-lvextend -l +50%FREE /dev/rootvg/tmplv
-xfs_growfs /tmp
-
-lvextend -l +100%FREE /dev/rootvg/varlv
-xfs_growfs /var
-
-rpm --import https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-8
-rpm --import https://packages.microsoft.com/keys/microsoft.asc
-
-yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
-
-cat >/etc/yum.repos.d/azure.repo <<'EOF'
-[azure-cli]
-name=azure-cli
-baseurl=https://packages.microsoft.com/yumrepos/azure-cli
-enabled=yes
-gpgcheck=yes
-EOF
-
-yum -y install azure-cli podman podman-docker jq gcc gpgme-devel libassuan-devel git make tmpwatch python3-devel htop go-toolset-1.17.12-1.module+el8.6.0+16014+a372c00b openvpn
-
-# Suppress emulation output for podman instead of docker for az acr compatability
-mkdir -p /etc/containers/
-touch /etc/containers/nodocker
-
-VSTS_AGENT_VERSION=2.206.1
-mkdir /home/cloud-user/agent
-pushd /home/cloud-user/agent
-curl -s https://vstsagentpackage.azureedge.net/agent/${VSTS_AGENT_VERSION}/vsts-agent-linux-x64-${VSTS_AGENT_VERSION}.tar.gz | tar -xz
-chown -R cloud-user:cloud-user .
-
-./bin/installdependencies.sh
-sudo -u cloud-user ./config.sh --unattended --url https://dev.azure.com/msazure --auth pat --token "$CIAZPTOKEN" --pool "$CIPOOLNAME" --agent "ARO-RHEL-$HOSTNAME" --replace
-./svc.sh install cloud-user
-popd
-
-cat >/home/cloud-user/agent/.path <<'EOF'
-/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/cloud-user/.local/bin:/home/cloud-user/bin
-EOF
-
-# Set the agent's "System capabilities" for tests (go-1.17 and GOLANG_FIPS) in the agent's .env file
-# and add a HACK for XDG_RUNTIME_DIR: https://github.com/containers/podman/issues/427
-cat >/home/cloud-user/agent/.env <<'EOF'
-go-1.17=true
-GOLANG_FIPS=1
-XDG_RUNTIME_DIR=/run/user/1000
-EOF
-
-cat >/etc/cron.weekly/yumupdate <<'EOF'
-#!/bin/bash
-
-yum update -y
-EOF
-chmod +x /etc/cron.weekly/yumupdate
-
-cat >/etc/cron.hourly/tmpwatch <<'EOF'
-#!/bin/bash
-
-exec /sbin/tmpwatch 24h /tmp
-EOF
-chmod +x /etc/cron.hourly/tmpwatch
-
-# HACK - podman doesn't always terminate or clean up it's pause.pid file causing
-# 'cannot reexec errors' so attempt to clean it up every minute to keep pipelines running
-# smoothly
-cat >/usr/local/bin/fix-podman-pause.sh <<'EOF'
-#!/bin/bash
-
-PAUSE_FILE='/tmp/podman-run-1000/libpod/tmp/pause.pid'
-
-if [ -f "${PAUSE_FILE}" ]; then
-	PID=$(cat ${PAUSE_FILE})
-	if ! ps -p $PID > /dev/null; then
-		rm $PAUSE_FILE
-	fi
-fi
-EOF
-chmod +x /usr/local/bin/fix-podman-pause.sh
-
-# HACK - /tmp will fill up causing build failures
-# delete anything not accessed within 2 days
-cat >/usr/local/bin/clean-tmp.sh <<'EOF'
-#!/bin/bash
-
-find /tmp -type f \( ! -user root \) -atime +2 -delete
-
-EOF
-chmod +x /usr/local/bin/clean-tmp.sh
-
-echo "0 0 */1 * * /usr/local/bin/clean-tmp.sh" >> cron
-echo "* * * * * /usr/local/bin/fix-podman-pause.sh" >> cron
-
-# HACK - https://github.com/containers/podman/issues/9002
-echo "@reboot loginctl enable-linger cloud-user" >> cron
-
-crontab cron
-rm cron
-
-(sleep 30; reboot) &
diff --git a/pkg/deploy/generator/scripts/devProxyVMSS.sh b/pkg/deploy/generator/scripts/devProxyVMSS.sh
index 6fc855646..d603a4535 100644
--- a/pkg/deploy/generator/scripts/devProxyVMSS.sh
+++ b/pkg/deploy/generator/scripts/devProxyVMSS.sh
@@ -2,7 +2,7 @@ echo "running RHUI fix"
 yum update -y --disablerepo='*' --enablerepo='rhui-microsoft-azure*'
 
 yum -y -x WALinuxAgent -x WALinuxAgent-udev update
-yum -y install docker
+yum -y install podman-docker
 
 firewall-cmd --add-port=443/tcp --permanent
 
@@ -16,7 +16,10 @@ cat >/root/.docker/config.json <<EOF
 	}
 }
 EOF
-systemctl start docker.service
+
+mkdir -p /etc/containers/
+touch /etc/containers/nodocker
+
 docker pull "$PROXYIMAGE"
 
 mkdir /etc/proxy
@@ -32,8 +35,8 @@ EOF
 
 cat >/etc/systemd/system/proxy.service <<'EOF'
 [Unit]
-After=docker.service
-Requires=docker.service
+After=network-online.target
+Wants=network-online.target
 
 [Service]
 EnvironmentFile=/etc/sysconfig/proxy
@@ -65,4 +68,7 @@ yum update -y
 EOF
 chmod +x /etc/cron.weekly/yumupdate
 
-(sleep 30; reboot) &
+(
+	sleep 30
+	reboot
+) &
diff --git a/pkg/deploy/generator/templates.go b/pkg/deploy/generator/templates.go
index e0540232a..2e6ec9f29 100644
--- a/pkg/deploy/generator/templates.go
+++ b/pkg/deploy/generator/templates.go
@@ -43,7 +43,6 @@ func (g *generator) templateFixup(t *arm.Template) ([]byte, error) {
 
 	// :-(
 	b = bytes.ReplaceAll(b, []byte(tenantIDHack), []byte("[subscription().tenantId]"))
-	b = bytes.ReplaceAll(b, []byte(`"capacity": 1337`), []byte(`"capacity": "[parameters('ciCapacity')]"`))
 	b = bytes.ReplaceAll(b, []byte(`"capacity": 1338`), []byte(`"capacity": "[parameters('rpVmssCapacity')]"`))
 	b = bytes.ReplaceAll(b, []byte(`"capacity": 1339`), []byte(`"capacity": "[parameters('gatewayVmssCapacity')]"`))
 	// pickZones doesn't work for regions that don't have zones.  We have created param nonZonalRegions in both rp and gateway and set default values to include all those regions.  It cannot be passed in-line to contains function, has to be created as an array in a parameter :(
@@ -57,7 +56,7 @@ func (g *generator) templateFixup(t *arm.Template) ([]byte, error) {
 		b = regexp.MustCompile(`(?m)"accessPolicies": \[[^]]*`+portalAccessPolicyHack+`[^]]*\]`).ReplaceAll(b, []byte(`"accessPolicies": "[concat(variables('portalKeyvaultAccessPolicies'), parameters('extraPortalKeyvaultAccessPolicies'))]"`))
 		b = regexp.MustCompile(`(?m)"accessPolicies": \[[^]]*`+serviceAccessPolicyHack+`[^]]*\]`).ReplaceAll(b, []byte(`"accessPolicies": "[concat(variables('serviceKeyvaultAccessPolicies'), parameters('extraServiceKeyvaultAccessPolicies'))]"`))
 		b = bytes.Replace(b, []byte(`"isVirtualNetworkFilterEnabled": true`), []byte(`"isVirtualNetworkFilterEnabled": "[not(parameters('disableCosmosDBFirewall'))]"`), 1)
-		b = bytes.Replace(b, []byte(`"virtualNetworkRules": []`), []byte(`"virtualNetworkRules": "[if(parameters('disableCosmosDBFirewall'), createArray(), if(startsWith(toLower(replace(resourceGroup().location, ' ', '')), 'usgov'), concat(variables('rpCosmoDbVirtualNetworkRules'), variables('rpCosmoDbVirtualNetworkRulesVmDeploy')), variables('rpCosmoDbVirtualNetworkRules')))]"`), 1)
+		b = bytes.Replace(b, []byte(`"virtualNetworkRules": []`), []byte(`"virtualNetworkRules": "[if(parameters('disableCosmosDBFirewall'), createArray(), variables('rpCosmoDbVirtualNetworkRules'))]"`), 1)
 		b = bytes.Replace(b, []byte(`"ipRules": []`), []byte(`"ipRules": "[if(parameters('disableCosmosDBFirewall'), createArray(), concat(parameters('ipRules'),createArray(createObject('ipAddressOrRange', '104.42.195.92'),createObject('ipAddressOrRange','40.76.54.131'),createObject('ipAddressOrRange','52.176.6.30'),createObject('ipAddressOrRange','52.169.50.45'),createObject('ipAddressOrRange','52.187.184.26'))))]"`), 1)
 		b = bytes.Replace(b, []byte(`"sourceAddressPrefixes": []`), []byte(`"sourceAddressPrefixes": "[parameters('rpNsgPortalSourceAddressPrefixes')]"`), 1)
 	}
diff --git a/pkg/deploy/generator/templates_dev.go b/pkg/deploy/generator/templates_dev.go
index 52f51f683..7f3bd4b00 100644
--- a/pkg/deploy/generator/templates_dev.go
+++ b/pkg/deploy/generator/templates_dev.go
@@ -33,7 +33,6 @@ func (g *generator) devSharedTemplate() *arm.Template {
 		g.devVnet(),
 		g.devVPNVnet(),
 		g.devVPN(),
-		g.devCIPool(),
 		g.devDiskEncryptionKeyvault(),
 		g.devDiskEncryptionKey(),
 		g.devDiskEncryptionKeyVaultAccessPolicy(),
@@ -81,9 +80,6 @@ func (g *generator) devSharedTemplate() *arm.Template {
 		))
 
 	for _, param := range []string{
-		"ciAzpToken",
-		"ciCapacity",
-		"ciPoolName",
 		"proxyCert",
 		"proxyClientCert",
 		"proxyDomainNameLabel",
@@ -98,11 +94,6 @@ func (g *generator) devSharedTemplate() *arm.Template {
 		typ := "string"
 		var defaultValue interface{}
 		switch param {
-		case "ciAzpToken", "ciPoolName":
-			defaultValue = ""
-		case "ciCapacity":
-			typ = "int"
-			defaultValue = 0
 		case "proxyImageAuth", "proxyKey":
 			typ = "securestring"
 		case "publicIPAddressAllocationMethod":
diff --git a/pkg/deploy/generator/templates_rp.go b/pkg/deploy/generator/templates_rp.go
index c3b3e2495..7381f29b5 100644
--- a/pkg/deploy/generator/templates_rp.go
+++ b/pkg/deploy/generator/templates_rp.go
@@ -148,8 +148,6 @@ func (g *generator) rpTemplate() *arm.Template {
 				{
 					ID: to.StringPtr("[resourceId(parameters('gatewayResourceGroupName'), 'Microsoft.Network/virtualNetworks/subnets', 'gateway-vnet', 'gateway-subnet')]"),
 				},
-			},
-			"rpCosmoDbVirtualNetworkRulesVmDeploy": &[]mgmtdocumentdb.VirtualNetworkRule{
 				{
 					ID: to.StringPtr("[resourceId('Microsoft.Network/virtualNetworks/subnets', 'aks-net', 'ClusterSubnet-001')]"),
 					// TODO: AKS Sharding: add rules for additional AKS shards for this RP instance. Currently only shard 1, which has subnet ClusterSubnet-001, is set above.
diff --git a/pkg/operator/controllers/checkers/ingresscertificatechecker/checker.go b/pkg/operator/controllers/checkers/ingresscertificatechecker/checker.go
index bd2ee0359..4bf2a3292 100644
--- a/pkg/operator/controllers/checkers/ingresscertificatechecker/checker.go
+++ b/pkg/operator/controllers/checkers/ingresscertificatechecker/checker.go
@@ -13,24 +13,23 @@ package ingresscertificatechecker
 import (
 	"context"
 	"errors"
-	"fmt"
 
 	configv1 "github.com/openshift/api/config/v1"
 	operatorv1 "github.com/openshift/api/operator/v1"
+	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/types"
 	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
+	utilnet "github.com/Azure/ARO-RP/pkg/util/net"
 )
 
+const ingressNameSuffix = "-ingress"
+
 var (
-	// errNoDefaultCertificate means a cluster has no default cert reference.
-	// This can happen because of the following reasons:
-	//   1. A cluster doesn't use a managed domain.
-	//    	For example it was created with a custom domain)
-	//   	or in a dev env where we don't have managed domains.
-	//   2. When a customer changed the ingress config incorrectly.
-	//
-	// While the first is valid the second is something we should be aware of.
-	errNoDefaultCertificate = errors.New("ingress has no default certificate set")
+	errNoCertificateAndCustomDomain       = errors.New("missing ingress certificate for cluster with custom domain")
+	errNoCertificateAndManagedDomain      = errors.New("missing ingress certificate for cluster with managed domain")
+	errInvalidCertificateAndManagedDomain = errors.New("invalid ingress certificate name for cluster with managed domain")
 )
 
 type ingressCertificateChecker interface {
@@ -48,25 +47,69 @@ func newIngressCertificateChecker(client client.Client) *checker {
 }
 
 func (r *checker) Check(ctx context.Context) error {
-	cv := &configv1.ClusterVersion{}
-	err := r.client.Get(ctx, types.NamespacedName{Name: "version"}, cv)
+	cv, err := r.clusterVersion(ctx)
 	if err != nil {
 		return err
 	}
 
-	ingress := &operatorv1.IngressController{}
-	err = r.client.Get(ctx, types.NamespacedName{Namespace: "openshift-ingress-operator", Name: "default"}, ingress)
+	ingress, err := r.ingress(ctx)
 	if err != nil {
 		return err
 	}
 
-	if ingress.Spec.DefaultCertificate == nil {
-		return errNoDefaultCertificate
+	clusterHasManagedDomain, err := r.clusterHasManagedDomain(ctx)
+	if err != nil {
+		return err
 	}
+	return validateCertificate(cv.Spec.ClusterID, ingress.Spec.DefaultCertificate, clusterHasManagedDomain)
+}
 
-	if ingress.Spec.DefaultCertificate.Name != string(cv.Spec.ClusterID)+"-ingress" {
-		return fmt.Errorf("custom ingress certificate in use: %q", ingress.Spec.DefaultCertificate.Name)
+func (r *checker) ingress(ctx context.Context) (*operatorv1.IngressController, error) {
+	ingress := &operatorv1.IngressController{}
+	err := r.client.Get(ctx, types.NamespacedName{Namespace: "openshift-ingress-operator", Name: "default"}, ingress)
+	if err != nil {
+		return nil, err
+	}
+	return ingress, nil
+}
+
+func (r *checker) clusterVersion(ctx context.Context) (*configv1.ClusterVersion, error) {
+	cv := &configv1.ClusterVersion{}
+	err := r.client.Get(ctx, types.NamespacedName{Name: "version"}, cv)
+	if err != nil {
+		return nil, err
+	}
+	return cv, nil
+}
+
+func (r *checker) clusterHasManagedDomain(ctx context.Context) (bool, error) {
+	cluster := &arov1alpha1.Cluster{}
+	err := r.client.Get(ctx, types.NamespacedName{Name: arov1alpha1.SingletonClusterName}, cluster)
+	if err != nil {
+		return false, err
+	}
+
+	return utilnet.IsManagedDomain(cluster.Spec.Domain), nil
+}
+
+func validateCertificate(clusterId configv1.ClusterID, ingressCertificate *corev1.LocalObjectReference, clusterHasManagedDomain bool) error {
+	if ingressCertificate == nil {
+		if clusterHasManagedDomain {
+			return errNoCertificateAndManagedDomain
+		}
+		return errNoCertificateAndCustomDomain
+	}
+
+	certificateName := ingressCertificate.Name
+
+	if !certificateIsValid(certificateName, clusterId) && clusterHasManagedDomain {
+		return errInvalidCertificateAndManagedDomain
 	}
 
 	return nil
 }
+
+func certificateIsValid(certificateName string, clusterID configv1.ClusterID) bool {
+	expectedCertName := string(clusterID) + ingressNameSuffix
+	return certificateName == expectedCertName
+}
diff --git a/pkg/operator/controllers/checkers/ingresscertificatechecker/controller.go b/pkg/operator/controllers/checkers/ingresscertificatechecker/controller.go
index 3adbd12e8..416574a18 100644
--- a/pkg/operator/controllers/checkers/ingresscertificatechecker/controller.go
+++ b/pkg/operator/controllers/checkers/ingresscertificatechecker/controller.go
@@ -37,39 +37,40 @@ const (
 
 // Reconciler runs a number of checkers
 type Reconciler struct {
-	log  *logrus.Entry
-	role string
-
+	log     *logrus.Entry
+	role    string
+	client  client.Client
 	checker ingressCertificateChecker
-
-	client client.Client
 }
 
 func NewReconciler(log *logrus.Entry, client client.Client, role string) *Reconciler {
 	return &Reconciler{
-		log:  log,
-		role: role,
-
+		log:     log,
+		role:    role,
+		client:  client,
 		checker: newIngressCertificateChecker(client),
-
-		client: client,
 	}
 }
 
 // Reconcile will keep checking that the cluster has a valid default IngressController configuration.
 func (r *Reconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.Result, error) {
-	instance := &arov1alpha1.Cluster{}
-	err := r.client.Get(ctx, types.NamespacedName{Name: arov1alpha1.SingletonClusterName}, instance)
+	cluster := &arov1alpha1.Cluster{}
+	err := r.client.Get(ctx, types.NamespacedName{Name: arov1alpha1.SingletonClusterName}, cluster)
 	if err != nil {
 		return reconcile.Result{}, err
 	}
 
-	if !instance.Spec.OperatorFlags.GetSimpleBoolean(checkercommon.ControllerEnabled) {
+	if !cluster.Spec.OperatorFlags.GetSimpleBoolean(checkercommon.ControllerEnabled) {
 		r.log.Debug("controller is disabled")
 		return r.reconcileDisabled(ctx)
 	}
 
 	r.log.Debug("running")
+
+	if err != nil {
+		return reconcile.Result{}, err
+	}
+
 	checkErr := r.checker.Check(ctx)
 	condition := r.condition(checkErr)
 
@@ -81,7 +82,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.
 	// In case of this error we want to set condition to False, but
 	// we don't want to continuously try to reconcile it as it might
 	// be expected config in some cases (e.g. custom domain cluster)
-	if errors.Is(checkErr, errNoDefaultCertificate) {
+	if errors.Is(checkErr, errNoCertificateAndCustomDomain) {
 		return reconcile.Result{RequeueAfter: time.Hour}, nil
 	}
 
diff --git a/pkg/util/azureclient/authz/remotepdp/client.go b/pkg/util/azureclient/authz/remotepdp/client.go
new file mode 100644
index 000000000..877449002
--- /dev/null
+++ b/pkg/util/azureclient/authz/remotepdp/client.go
@@ -0,0 +1,92 @@
+package remotepdp
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the Apache License 2.0.
+
+import (
+	"context"
+	"encoding/json"
+	"net/http"
+
+	"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"
+)
+
+// this asserts that &remotePDPClient{} would always implement RemotePDPClient
+var _ RemotePDPClient = &remotePDPClient{}
+
+// RemotePDPClient represents the Microsoft Remote PDP API Spec
+type RemotePDPClient interface {
+	CheckAccess(context.Context, AuthorizationRequest) (*AuthorizationDecisionResponse, error)
+}
+
+// remotePDPClient implements RemotePDPClient
+type remotePDPClient struct {
+	endpoint string
+	pipeline runtime.Pipeline
+}
+
+// NewRemotePDPClient returns an implementation of RemotePDPClient
+// endpoint - the fqdn of the regional specific endpoint of PDP
+// scope - the oauth scope required by the PDP server
+// cred - the credential of the client to call the PDP server
+func NewRemotePDPClient(endpoint, scope string, cred azcore.TokenCredential) *remotePDPClient {
+	authPolicy := runtime.NewBearerTokenPolicy(cred, []string{scope}, nil)
+
+	pipeline := runtime.NewPipeline(
+		modulename,
+		version,
+		runtime.PipelineOptions{
+			PerCall:  []policy.Policy{},
+			PerRetry: []policy.Policy{authPolicy},
+		},
+		nil,
+	)
+
+	return &remotePDPClient{endpoint, pipeline}
+}
+
+// CheckAccess sends an Authorization query to the PDP server specified in the client
+// ctx - the context to propagate
+// authzReq - the actual AuthorizationRequest
+func (r *remotePDPClient) CheckAccess(ctx context.Context, authzReq AuthorizationRequest) (*AuthorizationDecisionResponse, error) {
+	req, err := runtime.NewRequest(ctx, http.MethodPost, r.endpoint)
+	if err != nil {
+		return nil, err
+	}
+	runtime.MarshalAsJSON(req, authzReq)
+
+	res, err := r.pipeline.Do(req)
+	if err != nil {
+		return nil, err
+	}
+	if res.StatusCode != http.StatusOK {
+		return nil, newCheckAccessError(res)
+	}
+
+	var accessDecision AuthorizationDecisionResponse
+	if err := runtime.UnmarshalAsJSON(res, &accessDecision); err != nil {
+		return nil, err
+	}
+
+	return &accessDecision, nil
+}
+
+// newCheckAccessError returns an error when non HTTP 200 response is returned.
+func newCheckAccessError(r *http.Response) error {
+	payload, err := runtime.Payload(r)
+	if err != nil {
+		return err
+	}
+	var checkAccessError CheckAccessErrorResponse
+	err = json.Unmarshal(payload, &checkAccessError)
+	if err != nil {
+		return err
+	}
+	return &azcore.ResponseError{
+		StatusCode:  r.StatusCode,
+		RawResponse: r,
+		ErrorCode:   checkAccessError.Message,
+	}
+}
diff --git a/pkg/util/azureclient/authz/remotepdp/const.go b/pkg/util/azureclient/authz/remotepdp/const.go
new file mode 100644
index 000000000..73b4d32b6
--- /dev/null
+++ b/pkg/util/azureclient/authz/remotepdp/const.go
@@ -0,0 +1,20 @@
+package remotepdp
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the Apache License 2.0.
+
+const (
+	modulename = "aro-pdpclient"
+	// version is the semantic version of this module
+	version = "0.0.1"
+)
+
+// AccessDecision can be: Allowed, NotAllowed, Denied.
+type AccessDecision string
+
+// AccessDecision possible returned values
+const (
+	Allowed    AccessDecision = "Allowed"
+	NotAllowed AccessDecision = "NotAllowed"
+	Denied     AccessDecision = "Denied"
+)
diff --git a/pkg/util/azureclient/authz/remotepdp/types.go b/pkg/util/azureclient/authz/remotepdp/types.go
new file mode 100644
index 000000000..11399e5fe
--- /dev/null
+++ b/pkg/util/azureclient/authz/remotepdp/types.go
@@ -0,0 +1,98 @@
+package remotepdp
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the Apache License 2.0.
+
+// AuthorizationRequest represents the payload of the request sent to a PDP server
+type AuthorizationRequest struct {
+	Subject            SubjectInfo     `json:"Subject"`
+	Actions            []ActionInfo    `json:"Actions"`
+	Resource           ResourceInfo    `json:"Resource"`
+	Environment        EnvironmentInfo `json:"Environment,omitempty"`
+	CheckClassicAdmins bool            `json:"CheckClassicAdmins,omitempty"`
+}
+
+type SubjectInfo struct {
+	Attributes SubjectAttributes `json:"Attributes"`
+}
+
+// SubjectAttributes contains the possible attributes to describe the subject
+// of query (i.e. if IT has the access). The ObjectId field is the UUID value of
+// the subject and is required.
+type SubjectAttributes struct {
+	ObjectId         string   `json:"ObjectId"`
+	Groups           []string `json:"Groups,omitempty"`
+	ApplicationId    string   `json:"ApplicationId,omitempty"`
+	ApplicationACR   string   `json:"ApplicationACR,omitempty"`
+	RoleTemplate     []string `json:"RoleTemplate,omitempty"`
+	TenantId         string   `json:"tid,omitempty"`
+	Scope            string   `json:"Scope,omitempty"`
+	ResourceId       string   `json:"ResourceId,omitempty"`
+	Puid             string   `json:"puid,omitempty"`
+	AltSecId         string   `json:"altsecid,omitempty"`
+	IdentityProvider string   `json:"idp,omitempty"`
+	Issuer           string   `json:"iss,omitempty"`
+}
+
+// ActionInfo contains an action the query checks whether the subject
+// has access to perform. Example: "Microsoft.Network/virtualNetworks/read"
+type ActionInfo struct {
+	Id           string `json:"Id"`
+	IsDataAction bool   `json:"IsDataAction,omitempty"`
+	Attributes   `json:"Attributes"`
+}
+
+// ResourceInfo is the resource path of the target object the query
+// checks whether the subject has access to perform against it.
+type ResourceInfo struct {
+	Id         string `json:"Id"`
+	Attributes `json:"Attributes"`
+}
+
+type EnvironmentInfo struct {
+	Attributes `json:"Attributes"`
+}
+
+// AuthorizationDecisionResponse contains a paginated list of all decision results
+// In case the list is more than 50, follow NextLink to retrieve the next page.
+type AuthorizationDecisionResponse struct {
+	Value    []AuthorizationDecision `json:"value"`
+	NextLink string                  `json:"nextLink"`
+}
+
+// AuthorizationDecision tells whether the subject can perform the action
+// on the target resource.
+type AuthorizationDecision struct {
+	ActionId       string `json:"actionId,omitempty"`
+	AccessDecision `json:"accessDecision,omitempty"`
+	IsDataAction   bool `json:"isDataAction,omitempty"`
+	RoleAssignment `json:"roleAssignment,omitempty"`
+	DenyAssignment RoleDefinition `json:"denyAssignment,omitempty"`
+	TimeToLiveInMs int            `json:"timeToLiveInMs,omitempty"`
+}
+
+type RoleAssignment struct {
+	Id                                 string `json:"id,omitempty"`
+	RoleDefinitionId                   string `json:"roleDefinitionId,omitempty"`
+	PrincipalId                        string `json:"principalId,omitempty"`
+	PrincipalType                      string `json:"principaltype,omitempty"`
+	Scope                              string `json:"scope,omitempty"`
+	Condition                          string `json:"condition,omitempty"`
+	ConditionVersion                   string `json:"conditionVersion,omitempty"`
+	CanDelegate                        bool   `json:"canDelegate,omitempty"`
+	DelegatedManagedIdentityResourceId string `json:"deletegatedManagedIdentityResourceId,omitempty"`
+	Description                        string `json:"description,omitempty"`
+}
+
+type RoleDefinition struct {
+	Id string `json:"id,omitempty"`
+}
+
+type Attributes map[string]interface{}
+
+// RemotePDPErrorPayload represents the body content when the server returns
+// a non-successful error
+type CheckAccessErrorResponse struct {
+	StatusCode string `json:"statusCode,omitempty"`
+	Message    string `json:"message,omitempty"`
+}
diff --git a/pkg/util/azureclient/environments.go b/pkg/util/azureclient/environments.go
index 76a35fed7..eed6825fc 100644
--- a/pkg/util/azureclient/environments.go
+++ b/pkg/util/azureclient/environments.go
@@ -20,7 +20,15 @@ type AROEnvironment struct {
 	AppLensEndpoint          string
 	AppLensScope             string
 	AppLensTenantID          string
+	AuthzRemotePDPEndPoint   string
 	azidentity.AuthorityHost
+	AzureRbacPDPEnvironment
+}
+
+// AzureRbacPDPEnvironment contains cloud specific instance of Authz RBAC PDP Remote Server
+type AzureRbacPDPEnvironment struct {
+	Endpoint   string
+	OAuthScope string
 }
 
 var (
@@ -34,6 +42,10 @@ var (
 		AppLensScope:             "b9a1efcd-32ee-4330-834c-c04eb00f4b33",
 		AppLensTenantID:          "72f988bf-86f1-41af-91ab-2d7cd011db47",
 		AuthorityHost:            azidentity.AzurePublicCloud,
+		AzureRbacPDPEnvironment: AzureRbacPDPEnvironment{
+			Endpoint:   "https://%s.authorization.azure.net/providers/Microsoft.Authorization/checkAccess?api-version=2021-06-01-preview",
+			OAuthScope: "https://authorization.azure.net/.default",
+		},
 	}
 
 	// USGovernmentCloud contains additional ARO information for the US Gov cloud environment.
@@ -46,6 +58,10 @@ var (
 		AppLensScope:             "https://microsoft.onmicrosoft.com/runtimehost",
 		AppLensTenantID:          "cab8a31a-1906-4287-a0d8-4eef66b95f6e",
 		AuthorityHost:            azidentity.AzureGovernment,
+		AzureRbacPDPEnvironment: AzureRbacPDPEnvironment{
+			Endpoint:   "https://%s.authorization.azure.us/providers/Microsoft.Authorization/checkAccess?api-version=2021-06-01-preview",
+			OAuthScope: "https://authorization.azure.us/.default",
+		},
 	}
 )
 
diff --git a/pkg/util/dns/dns.go b/pkg/util/dns/dns.go
index d3f498f91..293b7ba06 100644
--- a/pkg/util/dns/dns.go
+++ b/pkg/util/dns/dns.go
@@ -18,7 +18,9 @@ import (
 	"github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/dns"
 )
 
-const resourceID = "resourceId"
+const (
+	resourceID = "resourceId"
+)
 
 type Manager interface {
 	Create(context.Context, *api.OpenShiftCluster) error
diff --git a/pkg/util/net/net.go b/pkg/util/net/net.go
index 5a39bdbbe..dc3198845 100644
--- a/pkg/util/net/net.go
+++ b/pkg/util/net/net.go
@@ -6,9 +6,15 @@ package net
 import (
 	"errors"
 	"net"
+	"strings"
 	"syscall"
 )
 
+const (
+	publicCloudManagedDomainSuffix = ".aroapp.io"
+	govCloudManagedDomainSuffix    = ".aroapp.azure.us"
+)
+
 // Listen returns a listener with its send and receive buffer sizes set, such
 // that sockets which are *returned* by the listener when Accept() is called
 // also have those buffer sizes.
@@ -72,3 +78,21 @@ func setBuffers(rc syscall.RawConn, sz int) error {
 
 	return err
 }
+
+func managedDomainSuffixes() []string {
+	return []string{
+		publicCloudManagedDomainSuffix,
+		govCloudManagedDomainSuffix,
+	}
+}
+
+func IsManagedDomain(domain string) bool {
+	suffixes := managedDomainSuffixes()
+	for _, suffix := range suffixes {
+		if strings.HasSuffix(domain, suffix) {
+			return true
+		}
+	}
+
+	return false
+}


Test patch:
diff --git a/pkg/operator/controllers/checkers/ingresscertificatechecker/checker_test.go b/pkg/operator/controllers/checkers/ingresscertificatechecker/checker_test.go
index c6817c4d2..bbba5edfb 100644
--- a/pkg/operator/controllers/checkers/ingresscertificatechecker/checker_test.go
+++ b/pkg/operator/controllers/checkers/ingresscertificatechecker/checker_test.go
@@ -11,81 +11,164 @@ import (
 	operatorv1 "github.com/openshift/api/operator/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
 	ctrlfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+	arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
 )
 
+type testData struct {
+	name              string
+	ingressController *operatorv1.IngressController
+	clusterVersion    *configv1.ClusterVersion
+	cluster           *arov1alpha1.Cluster
+	wantErr           string
+}
+
 func TestCheck(t *testing.T) {
+	const (
+		managedDomain = "foo.location.aroapp.io"
+		customDomain  = "custom.domain.io"
+	)
+
 	ctx := context.Background()
 
-	clusterVersion := &configv1.ClusterVersion{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: "version",
+	testCases := []testData{
+		{
+			name:           "Check returns clusterVersion not found error when ClusterVersion is nil",
+			clusterVersion: nil,
+			wantErr:        `clusterversions.config.openshift.io "version" not found`,
 		},
-		Spec: configv1.ClusterVersionSpec{
-			ClusterID: configv1.ClusterID("fake-cluster-id"),
+		{
+			name:              "Check returns IngressController not found error when IngressController is nil",
+			clusterVersion:    fakeClusterVersion(),
+			ingressController: nil,
+			wantErr:           `ingresscontrollers.operator.openshift.io "default" not found`,
+		},
+		{
+			name:              "Check returns Cluster not found error when Cluster is nil",
+			cluster:           nil,
+			clusterVersion:    fakeClusterVersion(),
+			ingressController: fakeIngressController(nil),
+			wantErr:           "clusters.aro.openshift.io \"cluster\" not found",
+		},
+		{
+			name:              "Check returns certificate error (ARO's fault) when no certificate is set and cluster has a managed domain",
+			clusterVersion:    fakeClusterVersion(),
+			ingressController: fakeIngressController(nil),
+			cluster:           fakeCluster(managedDomain),
+			wantErr:           errNoCertificateAndManagedDomain.Error(),
+		},
+		{
+			name:              "Check returns error (customer's fault) when no certificate is set and cluster has a custom domain",
+			clusterVersion:    fakeClusterVersion(),
+			ingressController: fakeIngressController(nil),
+			cluster:           fakeCluster(customDomain),
+			wantErr:           errNoCertificateAndCustomDomain.Error(),
+		},
+		{
+			name:           "Check returns error when cluster has a managed domain and there is an invalid certificate name",
+			clusterVersion: fakeClusterVersion(),
+			ingressController: fakeIngressController(&corev1.LocalObjectReference{
+				Name: "fake-custom-name-ingress",
+			}),
+			cluster: fakeCluster(managedDomain),
+			wantErr: errInvalidCertificateAndManagedDomain.Error(),
 		},
-	}
-
-	for _, tt := range []struct {
-		name              string
-		ingressController *operatorv1.IngressController
-		clusterVersion    *configv1.ClusterVersion
-		wantErr           string
-	}{
 		{
-			name:           "default certificate is set",
-			clusterVersion: clusterVersion,
+			name:           "Check returns no error when cluster has a managed domain and certificate has a valid name",
+			clusterVersion: fakeClusterVersion(),
 			ingressController: fakeIngressController(&corev1.LocalObjectReference{
 				Name: "fake-cluster-id-ingress",
 			}),
+			cluster: fakeCluster(managedDomain),
 		},
 		{
-			name:           "unexpected certificate name",
-			clusterVersion: clusterVersion,
+			name:           "Check returns error when cluster has a managed domain and certificate name is empty",
+			clusterVersion: fakeClusterVersion(),
 			ingressController: fakeIngressController(&corev1.LocalObjectReference{
-				Name: "fake-custom-name-ingress",
+				Name: "",
 			}),
-			wantErr: `custom ingress certificate in use: "fake-custom-name-ingress"`,
+			cluster: fakeCluster(managedDomain),
+			wantErr: errInvalidCertificateAndManagedDomain.Error(),
 		},
 		{
-			name:              "no default certificate set",
-			clusterVersion:    clusterVersion,
-			ingressController: fakeIngressController(nil),
-			wantErr:           "ingress has no default certificate set",
+			name:           "Check returns error when cluster has a managed domain and certificate name is just the ingress suffix",
+			clusterVersion: fakeClusterVersion(),
+			ingressController: fakeIngressController(&corev1.LocalObjectReference{
+				Name: "-ingress",
+			}),
+			cluster: fakeCluster(managedDomain),
+			wantErr: errInvalidCertificateAndManagedDomain.Error(),
 		},
 		{
-			name:           "missing IngressController",
-			clusterVersion: clusterVersion,
-			wantErr:        `ingresscontrollers.operator.openshift.io "default" not found`,
+			name:           "Check returns no error when we do not have a managed domain and certificate name is empty",
+			clusterVersion: fakeClusterVersion(),
+			ingressController: fakeIngressController(&corev1.LocalObjectReference{
+				Name: "",
+			}),
+			cluster: fakeCluster(customDomain),
 		},
 		{
-			name:    "missing ClusterVersion",
-			wantErr: `clusterversions.config.openshift.io "version" not found`,
+			name:           "Check returns no error when we do not have a managed domain and certificate name is just the suffix",
+			clusterVersion: fakeClusterVersion(),
+			ingressController: fakeIngressController(&corev1.LocalObjectReference{
+				Name: "-ingress",
+			}),
+			cluster: fakeCluster(customDomain),
 		},
-	} {
-		t.Run(tt.name, func(t *testing.T) {
-			clientBuilder := ctrlfake.NewClientBuilder()
-			if tt.clusterVersion != nil {
-				clientBuilder = clientBuilder.WithObjects(tt.clusterVersion)
-			}
-
-			if tt.ingressController != nil {
-				clientBuilder = clientBuilder.WithObjects(tt.ingressController)
-			}
+		{
+			name:           "Check returns no error when we do not have a managed domain and certificate name is invalid",
+			clusterVersion: fakeClusterVersion(),
+			ingressController: fakeIngressController(&corev1.LocalObjectReference{
+				Name: "invalid-ingress-name",
+			}),
+			cluster: fakeCluster(customDomain),
+		},
+	}
 
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
 			sp := &checker{
-				client: clientBuilder.Build(),
+				client: buildFakeClient(tt),
 			}
 
 			err := sp.Check(ctx)
-			if err != nil && err.Error() != tt.wantErr ||
-				err == nil && tt.wantErr != "" {
-				t.Errorf("\n%s\n !=\n%s", err, tt.wantErr)
+			if err != nil && err.Error() != tt.wantErr || err == nil && tt.wantErr != "" {
+				t.Errorf("\nExpected:\n%s \nbut got:\n%s", tt.wantErr, err)
 			}
 		})
 	}
 }
 
+func buildFakeClient(testCase testData) client.WithWatch {
+	clientBuilder := ctrlfake.NewClientBuilder()
+	if testCase.clusterVersion != nil {
+		clientBuilder = clientBuilder.WithObjects(testCase.clusterVersion)
+	}
+
+	if testCase.ingressController != nil {
+		clientBuilder = clientBuilder.WithObjects(testCase.ingressController)
+	}
+
+	if testCase.cluster != nil {
+		clientBuilder = clientBuilder.WithObjects(testCase.cluster)
+	}
+
+	return clientBuilder.Build()
+}
+
+func fakeClusterVersion() *configv1.ClusterVersion {
+	return &configv1.ClusterVersion{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "version",
+		},
+		Spec: configv1.ClusterVersionSpec{
+			ClusterID: configv1.ClusterID("fake-cluster-id"),
+		},
+	}
+}
+
 func fakeIngressController(certificateRef *corev1.LocalObjectReference) *operatorv1.IngressController {
 	return &operatorv1.IngressController{
 		ObjectMeta: metav1.ObjectMeta{
@@ -97,3 +180,14 @@ func fakeIngressController(certificateRef *corev1.LocalObjectReference) *operato
 		},
 	}
 }
+
+func fakeCluster(domain string) *arov1alpha1.Cluster {
+	return &arov1alpha1.Cluster{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: arov1alpha1.SingletonClusterName,
+		},
+		Spec: arov1alpha1.ClusterSpec{
+			Domain: domain,
+		},
+	}
+}
diff --git a/pkg/operator/controllers/checkers/ingresscertificatechecker/controller_test.go b/pkg/operator/controllers/checkers/ingresscertificatechecker/controller_test.go
index fa242b2a7..b0567f1cf 100644
--- a/pkg/operator/controllers/checkers/ingresscertificatechecker/controller_test.go
+++ b/pkg/operator/controllers/checkers/ingresscertificatechecker/controller_test.go
@@ -5,7 +5,6 @@ package ingresscertificatechecker
 
 import (
 	"context"
-	"errors"
 	"reflect"
 	"testing"
 	"time"
@@ -25,10 +24,12 @@ import (
 	_ "github.com/Azure/ARO-RP/pkg/util/scheme"
 )
 
-type fakeChecker func(ctx context.Context) error
+type fakeChecker struct {
+	expectedResult error
+}
 
 func (fc fakeChecker) Check(ctx context.Context) error {
-	return fc(ctx)
+	return fc.expectedResult
 }
 
 func TestReconcile(t *testing.T) {
@@ -50,18 +51,10 @@ func TestReconcile(t *testing.T) {
 			wantResult:           reconcile.Result{RequeueAfter: time.Hour},
 		},
 		{
-			name:                 "check failed with an error",
-			wantConditionStatus:  operatorv1.ConditionFalse,
-			wantConditionMessage: "fake basic error",
-			checkerReturnErr:     errors.New("fake basic error"),
-			wantErr:              "fake basic error",
-			wantResult:           reconcile.Result{RequeueAfter: time.Hour},
-		},
-		{
-			name:                 "no default cert set",
+			name:                 "reconciler handles correctly any error returned from the reconciler.checker",
 			wantConditionStatus:  operatorv1.ConditionFalse,
-			wantConditionMessage: "ingress has no default certificate set",
-			checkerReturnErr:     errNoDefaultCertificate,
+			wantConditionMessage: errNoCertificateAndCustomDomain.Error(),
+			checkerReturnErr:     errNoCertificateAndCustomDomain,
 			wantResult:           reconcile.Result{RequeueAfter: time.Hour},
 		},
 		{
@@ -92,12 +85,10 @@ func TestReconcile(t *testing.T) {
 			clientFake := fake.NewClientBuilder().WithObjects(instance).Build()
 
 			r := &Reconciler{
-				log:  utillog.GetLogger(),
-				role: "master",
-				checker: fakeChecker(func(ctx context.Context) error {
-					return tt.checkerReturnErr
-				}),
-				client: clientFake,
+				log:     utillog.GetLogger(),
+				role:    "master",
+				checker: fakeChecker{expectedResult: tt.checkerReturnErr},
+				client:  clientFake,
 			}
 
 			result, err := r.Reconcile(ctx, ctrl.Request{})
diff --git a/pkg/util/azureclient/applens/applens_client_test.go b/pkg/util/azureclient/applens/applens_client_test.go
index 5da459114..7c3f8a09c 100644
--- a/pkg/util/azureclient/applens/applens_client_test.go
+++ b/pkg/util/azureclient/applens/applens_client_test.go
@@ -15,6 +15,8 @@ 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"
+
+	testhttp "github.com/Azure/ARO-RP/test/util/http"
 )
 
 func TestEnsureErrorIsGeneratedOnResponse(t *testing.T) {
@@ -27,11 +29,11 @@ func TestEnsureErrorIsGeneratedOnResponse(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	srv, close := NewTLSServer()
+	srv, close := testhttp.NewTLSServer()
 	defer close()
 	srv.SetResponse(
-		WithBody(jsonString),
-		WithStatusCode(404))
+		testhttp.WithBody(jsonString),
+		testhttp.WithStatusCode(404))
 
 	pl := runtime.NewPipeline("applenstest", "v1.0.0", runtime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
 	client := &Client{endpoint: srv.URL(), pipeline: pl}
@@ -51,10 +53,10 @@ func TestEnsureErrorIsGeneratedOnResponse(t *testing.T) {
 }
 
 func TestEnsureErrorIsNotGeneratedOnResponse(t *testing.T) {
-	srv, close := NewTLSServer()
+	srv, close := testhttp.NewTLSServer()
 	defer close()
 	srv.SetResponse(
-		WithStatusCode(200))
+		testhttp.WithStatusCode(200))
 
 	pl := runtime.NewPipeline("applenstest", "v1.0.0", runtime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
 	client := &Client{endpoint: srv.URL(), pipeline: pl}
@@ -65,10 +67,10 @@ func TestEnsureErrorIsNotGeneratedOnResponse(t *testing.T) {
 }
 
 func TestRequestEnricherIsCalled(t *testing.T) {
-	srv, close := NewTLSServer()
+	srv, close := testhttp.NewTLSServer()
 	defer close()
 	srv.SetResponse(
-		WithStatusCode(200))
+		testhttp.WithStatusCode(200))
 
 	pl := runtime.NewPipeline("applenstest", "v1.0.0", runtime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
 	client := &Client{endpoint: srv.URL(), pipeline: pl}
@@ -88,10 +90,10 @@ func TestRequestEnricherIsCalled(t *testing.T) {
 }
 
 func TestNoOptionsIsCalled(t *testing.T) {
-	srv, close := NewTLSServer()
+	srv, close := testhttp.NewTLSServer()
 	defer close()
 	srv.SetResponse(
-		WithStatusCode(200))
+		testhttp.WithStatusCode(200))
 
 	pl := runtime.NewPipeline("applenstest", "v1.0.0", runtime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
 	client := &Client{endpoint: srv.URL(), pipeline: pl}
@@ -103,7 +105,7 @@ func TestNoOptionsIsCalled(t *testing.T) {
 }
 
 func TestCreateRequest(t *testing.T) {
-	srv, close := NewTLSServer()
+	srv, close := testhttp.NewTLSServer()
 	defer close()
 	pl := runtime.NewPipeline("applenstest", "v1.0.0", runtime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
 	client := &Client{endpoint: srv.URL(), pipeline: pl}
@@ -123,10 +125,10 @@ func TestCreateRequest(t *testing.T) {
 }
 
 func TestSendPost(t *testing.T) {
-	srv, close := NewTLSServer()
+	srv, close := testhttp.NewTLSServer()
 	defer close()
 	srv.SetResponse(
-		WithStatusCode(200))
+		testhttp.WithStatusCode(200))
 	verifier := pipelineVerifier{}
 	pl := runtime.NewPipeline("applenstest", "v1.0.0", runtime.PipelineOptions{PerCall: []policy.Policy{&verifier}}, &policy.ClientOptions{Transport: srv})
 	client := &Client{endpoint: srv.URL(), pipeline: pl}
@@ -142,10 +144,10 @@ func TestSendPost(t *testing.T) {
 }
 
 func TestGetDetector(t *testing.T) {
-	srv, close := NewTLSServer()
+	srv, close := testhttp.NewTLSServer()
 	defer close()
 	srv.SetResponse(
-		WithStatusCode(200))
+		testhttp.WithStatusCode(200))
 	verifier := pipelineVerifier{}
 	pl := runtime.NewPipeline("applenstest", "v1.0.0", runtime.PipelineOptions{PerCall: []policy.Policy{&verifier}}, &policy.ClientOptions{Transport: srv})
 	client := &Client{endpoint: srv.URL(), pipeline: pl}
@@ -183,10 +185,10 @@ func TestGetDetector(t *testing.T) {
 }
 
 func TestListDetectors(t *testing.T) {
-	srv, close := NewTLSServer()
+	srv, close := testhttp.NewTLSServer()
 	defer close()
 	srv.SetResponse(
-		WithStatusCode(200))
+		testhttp.WithStatusCode(200))
 	verifier := pipelineVerifier{}
 	pl := runtime.NewPipeline("applenstest", "v1.0.0", runtime.PipelineOptions{PerCall: []policy.Policy{&verifier}}, &policy.ClientOptions{Transport: srv})
 	client := &Client{endpoint: srv.URL(), pipeline: pl}
diff --git a/pkg/util/azureclient/applens/applens_error_test.go b/pkg/util/azureclient/applens/applens_error_test.go
index 6464516ad..4bc71032c 100644
--- a/pkg/util/azureclient/applens/applens_error_test.go
+++ b/pkg/util/azureclient/applens/applens_error_test.go
@@ -14,13 +14,15 @@ 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"
+
+	testhttp "github.com/Azure/ARO-RP/test/util/http"
 )
 
 func TestAppLensErrorOnEmptyResponse(t *testing.T) {
-	srv, close := NewTLSServer()
+	srv, close := testhttp.NewTLSServer()
 	defer close()
 	srv.SetResponse(
-		WithStatusCode(404))
+		testhttp.WithStatusCode(404))
 
 	req, err := runtime.NewRequest(context.Background(), http.MethodGet, srv.URL())
 	if err != nil {
@@ -46,11 +48,11 @@ func TestAppLensErrorOnEmptyResponse(t *testing.T) {
 }
 
 func TestAppLensErrorOnNonJsonBody(t *testing.T) {
-	srv, close := NewTLSServer()
+	srv, close := testhttp.NewTLSServer()
 	defer close()
 	srv.SetResponse(
-		WithBody([]byte("This is not JSON")),
-		WithStatusCode(404))
+		testhttp.WithBody([]byte("This is not JSON")),
+		testhttp.WithStatusCode(404))
 
 	req, err := runtime.NewRequest(context.Background(), http.MethodGet, srv.URL())
 	if err != nil {
@@ -88,11 +90,11 @@ func TestAppLensErrorOnJsonBody(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	srv, close := NewTLSServer()
+	srv, close := testhttp.NewTLSServer()
 	defer close()
 	srv.SetResponse(
-		WithBody(jsonString),
-		WithStatusCode(404))
+		testhttp.WithBody(jsonString),
+		testhttp.WithStatusCode(404))
 
 	req, err := runtime.NewRequest(context.Background(), http.MethodGet, srv.URL())
 	if err != nil {
diff --git a/pkg/util/azureclient/authz/remotepdp/client_test.go b/pkg/util/azureclient/authz/remotepdp/client_test.go
new file mode 100644
index 000000000..dcc17849a
--- /dev/null
+++ b/pkg/util/azureclient/authz/remotepdp/client_test.go
@@ -0,0 +1,74 @@
+package remotepdp
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the Apache License 2.0.
+
+import (
+	"context"
+	"errors"
+	"net/http"
+	"testing"
+
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
+	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
+
+	testhttp "github.com/Azure/ARO-RP/test/util/http"
+)
+
+func TestClientCreate(t *testing.T) {
+	endpoint := "https://westus.authorization.azure.net/providers/Microsoft.Authorization/checkAccess?api-version=2021-06-01-preview"
+	scope := "https://authorization.azure.net/.default"
+	cred, err := azidentity.NewClientSecretCredential("888988bf-86f1-31ea-91cd-2d7cd011db48", "clientID", "clientSecret", nil)
+	if err != nil {
+		t.Error("Unable to create a new PDP client")
+	}
+	client := NewRemotePDPClient(endpoint, scope, cred)
+	if client.endpoint != endpoint {
+		t.Error("The client endpoint doesn't equal to the target endpoint")
+	}
+}
+
+func TestCallingCheckAccess(t *testing.T) {
+	cases := []struct {
+		desc             string
+		returnedHttpCode int
+		expectedDecision *AuthorizationDecisionResponse
+		expectedErr      error
+	}{
+		{
+			desc:             "Successful calls should return an access decision",
+			returnedHttpCode: http.StatusOK,
+			expectedDecision: &AuthorizationDecisionResponse{},
+			expectedErr:      nil,
+		}, {
+			desc:             "Call resulting in a failure should return an error",
+			returnedHttpCode: http.StatusUnauthorized,
+			expectedDecision: nil,
+			expectedErr:      errors.New("An error"),
+		},
+	}
+	for _, c := range cases {
+		srv, close := testhttp.NewTLSServer()
+		srv.SetResponse(testhttp.WithStatusCode(c.returnedHttpCode))
+		client := createClientWithServer(srv)
+		decision, err := client.CheckAccess(context.Background(), AuthorizationRequest{})
+		if decision != c.expectedDecision && err != c.expectedErr {
+			t.Errorf("%s: expected decision to be %v; and error to be %s. Got %v and %s",
+				c.desc, c.expectedDecision, c.expectedErr, decision, err)
+		}
+		close()
+	}
+}
+
+func createClientWithServer(s *testhttp.Server) RemotePDPClient {
+	return &remotePDPClient{
+		endpoint: s.URL(),
+		pipeline: runtime.NewPipeline(
+			"remotepdpclient_test",
+			"v1.0.0",
+			runtime.PipelineOptions{},
+			&policy.ClientOptions{Transport: s},
+		),
+	}
+}
diff --git a/pkg/util/net/net_test.go b/pkg/util/net/net_test.go
new file mode 100644
index 000000000..d2fd0fac2
--- /dev/null
+++ b/pkg/util/net/net_test.go
@@ -0,0 +1,46 @@
+package net_test
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the Apache License 2.0.
+
+import (
+	"testing"
+
+	utilnet "github.com/Azure/ARO-RP/pkg/util/net"
+)
+
+func TestIsManagedDomain(t *testing.T) {
+	type TestData struct {
+		testName string
+		domain   string
+		want     bool
+	}
+
+	testCases := []TestData{
+		{
+			testName: "Custom domain",
+			domain:   "custom.domain.io",
+			want:     false,
+		},
+		{
+			testName: "Public Cloud Managed Domain",
+			domain:   "foo.location.aroapp.io",
+			want:     true,
+		},
+		{
+			testName: "US Gov Cloud Managed Domain",
+			domain:   "foo.aroapp.azure.us",
+			want:     true,
+		},
+	}
+
+	for _, tt := range testCases {
+		t.Run(tt.testName, func(t *testing.T) {
+			got := utilnet.IsManagedDomain(tt.domain)
+
+			if got != tt.want {
+				t.Errorf("expected %v, but got %v", tt.want, got)
+			}
+		})
+	}
+}