# Setup Azure Management Cluster for HyperShift

!!! note "Developer Preview in OCP 4.21"

    Self-managed Azure HostedClusters are available as a Developer Preview feature in OpenShift Container Platform 4.21.

This document describes how to install the HyperShift operator on your Azure management cluster with optional External DNS configuration.

## DNS Management Options

You can set up your management cluster with or without External DNS:

!!! tip "Choose Your Approach"

    **With External DNS (Recommended for Production)**

    - Automatic DNS record management
    - Custom domain names (e.g., `api-cluster.example.com`)
    - Requires: DNS zones, service principal, External DNS operator
    - **Follow sections**: DNS Zone Configuration → External DNS Service Principal Setup → HyperShift Operator Installation (With External DNS)

    **Without External DNS (Simpler for Dev/Test)**

    - Manual or Azure-provided DNS
    - API server uses Azure LoadBalancer DNS (e.g., `abc123.region.cloudapp.azure.com`)
    - No DNS zones or service principals needed
    - **Skip to**: HyperShift Operator Installation (Without External DNS)

## Prerequisites

- Azure CLI (`az`) installed and configured
- OpenShift CLI (`oc`) or Kubernetes CLI (`kubectl`)
- An Azure OpenShift management cluster
- **For External DNS only**: `jq` command-line JSON processor

## DNS Zone Configuration (External DNS Only)

!!! warning "External DNS Only"

    This section is only required if you want automatic DNS management with External DNS. If you prefer a simpler setup, skip to [HyperShift Operator Installation (Without External DNS)](#hypershift-operator-installation-without-external-dns).

Before creating HostedClusters with External DNS, you need to set up DNS zones and delegate DNS records for your clusters.

!!! note "About PERSISTENT_RG_NAME"
    In Red Hat environments, a periodic Azure resource "reaper" deletes resources that are not properly tagged or not located in an approved resource group. We frequently use the `os4-common` resource group for shared, long-lived assets (for example, public DNS zones) to avoid accidental cleanup. If you are not in Red Hat infrastructure, set `PERSISTENT_RG_NAME` to any long-lived resource group in your subscription that will not be automatically reaped, or ensure your organization’s required tags/policies are applied. The name does not have to be `os4-common`—use whatever persistent resource group fits your environment.

```bash
# Set DNS configuration variables
PARENT_DNS_RG="your-parent-dns-rg"
PARENT_DNS_ZONE="your-parent.dns.zone.com"
DNS_RECORD_NAME="your-subdomain"
PERSISTENT_RG_NAME="os4-common"  # Use persistent resource group
DNS_ZONE_NAME="your-subdomain.your-parent.dns.zone.com"

az group create \  
      --name $PERSISTENT_RG_NAME \  
      --location $LOCATION  

az network dns zone create \  
        --resource-group $PERSISTENT_RG_NAME \  
        --name $DNS_ZONE_NAME  

# Delete existing NS record if it exists
az network dns record-set ns delete \
    --resource-group $PARENT_DNS_RG \
    --zone-name $PARENT_DNS_ZONE \
    --name $DNS_RECORD_NAME -y

# Get name servers from your DNS zone
name_servers=$(az network dns zone show \
    --resource-group $PERSISTENT_RG_NAME \
    --name $DNS_ZONE_NAME \
    --query nameServers \
    --output tsv)

# Create array of name servers
ns_array=()
while IFS= read -r ns; do
    ns_array+=("$ns")
done <<< "$name_servers"

# Add NS records to parent zone
for ns in "${ns_array[@]}"; do
    az network dns record-set ns add-record \
        --resource-group $PARENT_DNS_RG \
        --zone-name $PARENT_DNS_ZONE \
        --record-set-name $DNS_RECORD_NAME \
        --nsdname "$ns"
done
```

## External DNS Service Principal Setup (External DNS Only)

!!! warning "External DNS Only"

    This section is only required if you're using External DNS. Skip to [HyperShift Operator Installation (Without External DNS)](#hypershift-operator-installation-without-external-dns) if not.

Create a dedicated service principal for External DNS:

```bash
# Set External DNS configuration
EXTERNAL_DNS_NEW_SP_NAME="ExternalDnsServicePrincipal"
SERVICE_PRINCIPAL_FILEPATH="/path/to/azure_mgmt.json"
PERSISTENT_RG_NAME="hypershift-shared-resources"  # Use persistent resource group

# Create service principal for External DNS
DNS_SP=$(az ad sp create-for-rbac --name ${EXTERNAL_DNS_NEW_SP_NAME})
EXTERNAL_DNS_SP_APP_ID=$(echo "$DNS_SP" | jq -r '.appId')
EXTERNAL_DNS_SP_PASSWORD=$(echo "$DNS_SP" | jq -r '.password')

# Get DNS zone ID
DNS_ID=$(az network dns zone show \
    --name ${DNS_ZONE_NAME} \
    --resource-group ${PERSISTENT_RG_NAME} \
    --query "id" \
    --output tsv)

# Assign roles to the service principal
az role assignment create \
    --role "Reader" \
    --assignee "${EXTERNAL_DNS_SP_APP_ID}" \
    --scope "${DNS_ID}"

az role assignment create \
    --role "Contributor" \
    --assignee "${EXTERNAL_DNS_SP_APP_ID}" \
    --scope "${DNS_ID}"

# Create Azure credentials file
cat <<-EOF > ${SERVICE_PRINCIPAL_FILEPATH}
{
  "tenantId": "$(az account show --query tenantId -o tsv)",
  "subscriptionId": "$(az account show --query id -o tsv)",
  "resourceGroup": "$PERSISTENT_RG_NAME",
  "aadClientId": "$EXTERNAL_DNS_SP_APP_ID",
  "aadClientSecret": "$EXTERNAL_DNS_SP_PASSWORD"
}
EOF

# Create Kubernetes secret for Azure credentials
kubectl delete secret/azure-config-file --namespace "default" || true
kubectl create secret generic azure-config-file \
    --namespace "default" \
    --from-file ${SERVICE_PRINCIPAL_FILEPATH}
```

## HyperShift Operator Installation

Choose the installation method that matches your DNS approach:

### With External DNS

Install the HyperShift operator with External DNS configuration:

```bash
# Set installation variables
PULL_SECRET="/path/to/pull-secret.json"
HYPERSHIFT_BINARY_PATH="/path/to/hypershift/bin"

# Install HyperShift operator with External DNS
${HYPERSHIFT_BINARY_PATH}/hypershift install \
    --external-dns-provider=azure \
    --external-dns-credentials ${SERVICE_PRINCIPAL_FILEPATH} \
    --pull-secret ${PULL_SECRET} \
    --external-dns-domain-filter ${DNS_ZONE_NAME} \
    --limit-crd-install Azure
```

!!! tip "Custom HyperShift Image"

    Add `--hypershift-image quay.io/hypershift/hypershift:TAG` if using a custom operator image.

After installation, verify both the HyperShift operator and External DNS are running:

```bash
# Check HyperShift operator and ExternalDNS pods
oc get pods -n hypershift
```

You should see both `operator-*` and `external-dns-*` pods running.

### Without External DNS

Install the HyperShift operator without External DNS:

```bash
# Set installation variables
PULL_SECRET="/path/to/pull-secret.json"
HYPERSHIFT_BINARY_PATH="/path/to/hypershift/bin"

# Install HyperShift operator (no External DNS)
${HYPERSHIFT_BINARY_PATH}/hypershift install \
    --pull-secret ${PULL_SECRET} \
    --limit-crd-install Azure
```

!!! tip "Custom HyperShift Image"

    Add `--hypershift-image quay.io/hypershift/hypershift:TAG` if using a custom operator image.

After installation, verify the HyperShift operator is running:

```bash
# Check HyperShift operator pods
oc get pods -n hypershift
```

You should see the `operator-*` pod running (no `external-dns` pod).

## Verification

Verify your installation:

=== "With External DNS"

    ```bash
    # Check for both operator and external-dns
    oc get pods -n hypershift

    # Expected output:
    # NAME                           READY   STATUS    RESTARTS   AGE
    # external-dns-xxxxx-xxxxx       1/1     Running   0          1m
    # operator-xxxxx-xxxxx           1/1     Running   0          1m
    ```

=== "Without External DNS"

    ```bash
    # Check for operator only
    oc get pods -n hypershift

    # Expected output:
    # NAME                     READY   STATUS    RESTARTS   AGE
    # operator-xxxxx-xxxxx     1/1     Running   0          1m
    ```

## Private Cluster Support (Optional)

If you plan to create private clusters with Azure Private Link, the HyperShift operator
must be installed with additional flags for Private Link Service management. See
[Deploy Azure Private Clusters](deploy-azure-private-clusters.md) for the full guide,
but the key difference is adding these flags to the `hypershift install` command:

```bash
hypershift install \
    --private-platform Azure \
    --azure-private-creds /path/to/azure-private-credentials.json \
    --azure-pls-resource-group ${MGMT_INFRA_RG} \
    # ... include your standard install flags from above
```

!!! important

    The `--private-platform Azure` flag **must** be set during operator installation.
    If you install without it, you must re-run `hypershift install` with the private
    flags before creating any private clusters.

See [Deploy Azure Private Clusters - Step 2](deploy-azure-private-clusters.md#step-2-install-hypershift-operator-with-private-platform-support)
for complete details and alternative authentication methods.

## Next Steps

Once the management cluster is set up, create hosted clusters:

- [Create a Self-Managed Azure HostedCluster](create-self-managed-azure-cluster.md) - Includes guidance for both DNS approaches
- [Deploy Azure Private Clusters](deploy-azure-private-clusters.md) - Configure private endpoint access with Azure Private Link