Azure Red Hat OpenShift (ARO) is a fully-managed turnkey application platform.
Supports Public ARO clusters and Private ARO clusters.
Using the code in the repo will require having the following tools installed:
- The Terraform CLI (>= 1.12)
- The Azure CLI (
az) - The OC CLI (for cluster access)
Optional tools (for enhanced testing):
- tflint (for Terraform linting)
- checkov (for security scanning)
- git (for
make reference-sync)
Managed identities: reference/ is not tracked in git. Before make init, make pr, or make create*, populate it with the upstream module snapshot your environment documents:
REFERENCE_ARO_AZAPI_URL=https://github.com/your-org/terraform-aro-reference-aro-azapi.git make reference-syncSee CONTRIBUTING.md for CI variables (REFERENCE_ARO_AZAPI_URL) and optional knobs (REFERENCE_SYNC_AVM, branch refs).
-
Create a local variables file
make tfvars
-
Modify the
terraform.tfvarsvar file, you can use thevariables.tfto see the full list of variables that can be set.NOTE: You can define the subscription_id needed for the Auth using
export TF_VAR_subscription_id="xxx"as well. -
Deploy your cluster
make create
NOTE: By default the ingress_profile and the api_server_profile is both Public, but can be change using the TF variables.
NOTE: The
aro_versionvariable is optional. If not specified, the latest available ARO version for your region will be automatically detected usingaz aro get-versions -l <location>.
-
Modify the
terraform.tfvarsvar file, you can use thevariables.tfto see the full list of variables that can be set.make create-private
Jumphost SSH keys: If
jumphost_ssh_public_key_pathandjumphost_ssh_private_key_pathare both unset (null), Terraform generates an ED25519 keypair for the VM and publishes sensitive outputsjumphost_ssh_private_key_opensshandjumphost_ssh_public_key_openssh(material lives in state). To use keys from disk instead, set both paths; the private key must be unencrypted (provisioners do not support passphrase-protected keys).NOTE: restrict_egress_traffic=true will secure ARO cluster by routing Egress traffic through an Azure Firewall.
NOTE2: Private Clusters can be created without Public IP using the UserDefineRouting flag in the outboundtype=UserDefineRouting variable. By default LoadBalancer is used for the egress.
Azure Red Hat OpenShift supports managed identities (currently in tech preview) as an alternative to service principals. Managed identities provide enhanced security by eliminating the need to manage credentials.
Important Notes:
- This feature is currently in tech preview and not recommended for production use without your own validation
- Managed identity clusters use AzAPI and modules under
reference/aro-azapi(notazurerm_redhat_openshift_cluster) - Nine user-assigned identities and RBAC are created by those reference modules when
enable_managed_identities = true - Existing clusters using service principals cannot be migrated to managed identities in place
To enable managed identities:
Option 1: Using Makefile (Recommended)
Deploy a public cluster with managed identities:
make create-managed-identityDeploy a private cluster with managed identities:
make create-private-managed-identityOption 2: Using terraform.tfvars
-
Set
enable_managed_identities = truein yourterraform.tfvars:enable_managed_identities = true
-
Deploy your cluster as usual:
make create
When enable_managed_identities = true:
reference/aro-azapi/modules/managed_identitycreates nine user-assigned identities (including cluster MSI)- RBAC defaults to
reference/aro-azapi/modules/aro_role_assignmentswhenmi_use_builtin_operator_roles = true(built-in ARO operator / network roles). Setmi_use_builtin_operator_roles = falseto usemodules/aro-mi-rbac-legacy-networkinstead (optionalmi_minimal_network_rolefor custom network roles—see variable descriptions in01-variables.tf). modules/aro-cluster-azapideploys the cluster withplatformWorkloadIdentityProfilevia AzAPI- Outputs mirror the service-principal path (console/API URLs; IPs when exposed on the cluster resource)
Networking: With managed identities, 10-network.tf does not attach the BYO NSG to cluster subnets (azurerm_subnet_network_security_group_association count is 0). The AzAPI cluster therefore uses preconfiguredNSG Disabled so install matches subnet state. The service-principal path keeps subnet NSG associations and preconfigured_network_security_group_enabled = true.
UDR: outbound_type = "UserDefinedRouting" requires restrict_egress_traffic = true (validated at plan time) so the firewall route table and RBAC match what the cluster expects.
Terraform vs cluster updates (AzAPI): The vendored modules/aro-cluster-azapi resource uses lifecycle.ignore_changes on the request body (same pattern as the upstream reference) so routine API drift and secrets do not force destructive updates. Many cluster property changes will therefore not be applied by a subsequent terraform apply; treat substantive changes as replace workflows or out-of-band updates per Microsoft guidance.
For more information, see the Microsoft documentation on ARO managed identities.
After deploying your cluster, you can log in using the make login target:
make loginThis command will:
- Automatically retrieve cluster information from Terraform outputs
- Fetch kubeadmin credentials from Azure
- Log you into the OpenShift cluster using
oc login
If you prefer to log in manually or need to access cluster information directly:
-
Get the ARO cluster's api server URL.
ARO_URL=$(terraform output -raw api_url) echo $ARO_URL
-
Get the ARO cluster's Console URL
CONSOLE_URL=$(terraform output -raw console_url) echo $CONSOLE_URL
-
Get the ARO cluster's credentials.
CLUSTER_NAME=$(terraform output -raw cluster_name) RESOURCE_GROUP=$(terraform output -raw resource_group_name) ARO_USERNAME=$(az aro list-credentials -n $CLUSTER_NAME -g $RESOURCE_GROUP -o json | jq -r '.kubeadminUsername') ARO_PASSWORD=$(az aro list-credentials -n $CLUSTER_NAME -g $RESOURCE_GROUP -o json | jq -r '.kubeadminPassword') echo $ARO_PASSWORD echo $ARO_USERNAME
-
Log into the cluster using oc login command. ex.
oc login $ARO_URL -u $ARO_USERNAME -p $ARO_PASSWORD
Or simply use:
make login
-
Check that you can access the Console by opening the console url in your browser.
-
Save the jump host public IP address
JUMP_IP=$(terraform output -raw public_ip) echo $JUMP_IP
Or get it manually:
CLUSTER_NAME=$(terraform output -raw cluster_name) RESOURCE_GROUP=$(terraform output -raw resource_group_name) JUMP_IP=$(az vm list-ip-addresses -g $RESOURCE_GROUP -n $CLUSTER_NAME-jumphost -o tsv \ --query '[].virtualMachine.network.publicIpAddresses[0].ipAddress') echo $JUMP_IP
-
update /etc/hosts to point the openshift domains to localhost. Use the DNS of your openshift cluster as described in the previous step in place of $YOUR_OPENSHIFT_DNS below
127.0.0.1 api.$YOUR_OPENSHIFT_DNS 127.0.0.1 console-openshift-console.apps.$YOUR_OPENSHIFT_DNS 127.0.0.1 oauth-openshift.apps.$YOUR_OPENSHIFT_DNS
-
SSH to that instance, tunneling traffic for the appropriate hostnames. Be sure to use your new/existing private key, the OpenShift DNS for $YOUR_OPENSHIFT_DNS and your Jumphost IP
sudo ssh -L 6443:api.$YOUR_OPENSHIFT_DNS:6443 \ -L 443:console-openshift-console.apps.$YOUR_OPENSHIFT_DNS:443 \ -L 80:console-openshift-console.apps.$YOUR_OPENSHIFT_DNS:80 \ aro@$JUMP_IP
-
Log in using oc login
oc login $ARO_URL -u $ARO_USERNAME -p $ARO_PASSWORD
Or use the automated login command (works from your local machine if you have SSH tunnel set up):
make login
NOTE: Another option to connect to a Private ARO cluster jumphost is the usage of sshuttle. If we suppose that we deployed ARO vnet with the 10.0.0.0/20 CIDR we can connect to the cluster using (both API and Console):
sshuttle --dns -NHr aro@$JUMP_IP 10.0.0.0/20 --daemonand opening a browser the api.$YOUR_OPENSHIFT_DNS and console-openshift-console.apps.$YOUR_OPENSHIFT_DNS will be reachable.
Before committing changes, run the pre-commit checks:
make prThis will run:
- Terraform validate
- Terraform fmt check
- tflint (if installed)
- checkov security scan (if installed)
For a full test suite including terraform plan (requires Azure CLI login):
make testThis repository includes a GitHub Actions workflow that automatically runs pre-commit checks on:
- Pull requests to
main - Pushes to
main
The workflow will:
- Run
make prto validate code - Post a comment on PRs with check results
- Cache Terraform providers for faster runs
See .github/workflows/ci.yml for details.
make help- Print Makefile targets and one-line descriptionsmake tfvars- Create terraform.tfvars from examplemake init- Initialize Terraformmake create- Create public ARO clustermake create-private- Create private ARO cluster with egress restrictionmake create-private-noegress- Create private ARO cluster without egress restrictionmake create-managed-identity- Create public ARO cluster with managed identities (preview)make create-private-managed-identity- Create private ARO cluster with managed identities (preview)make login- Log into ARO cluster (requires cluster to be deployed)make destroy- Destroy service principal-based cluster (non-interactive, uses -auto-approve)make destroy-managed-identity- Destroy managed identity cluster (interactive, with wait/verification)make destroy-private-managed-identity- Alias formake destroy-managed-identity(private MI teardown is identical)make destroy-managed-identity.force- Destroy managed identity cluster (non-interactive, with wait/verification)make clean- Remove terraform state and providersmake validate- Run terraform validatemake fmt- Check terraform formattingmake fmt-fix- Fix terraform formattingmake check- Run validate and fmt checksmake lint- Run linting checksmake test- Run full test suite (requires Azure CLI)make pr- Run pre-commit checks (no Azure CLI needed)
This repository follows Semantic Versioning (MAJOR.MINOR.PATCH).
- MAJOR (1.0.0): Breaking changes (renamed variables, changed types, removed features)
- MINOR (0.2.0): New features (new variables/outputs, backward-compatible additions)
- PATCH (0.1.1): Bug fixes (fixes, documentation updates)
When ready to release a new version:
-
Ensure all tests pass:
make test -
Update CHANGELOG.md:
- Move all
[Unreleased]content to a new version section (e.g.,## [0.2.0] - YYYY-MM-DD) - Add link at bottom:
[0.2.0]: https://github.com/rh-mobb/terraform-aro/releases/tag/v0.2.0 - Update
[Unreleased]link:[Unreleased]: https://github.com/rh-mobb/terraform-aro/compare/v0.2.0...HEAD
- Move all
-
Update PLAN.md version (if applicable)
-
Commit changes:
git add CHANGELOG.md PLAN.md git commit -m "chore: prepare release v0.2.0" -
Create annotated git tag:
git tag -a v0.2.0 -m "Release v0.2.0: Brief description of changes" -
Push commits and tag:
git push origin main git push origin v0.2.0
-
Create GitHub Release (optional but recommended):
- Go to GitHub Releases page
- Click "Draft a new release"
- Select the tag (e.g.,
v0.2.0) - Copy CHANGELOG.md content as release notes
- Publish release
Note: During pre-1.0.0 phase, breaking changes can be in MINOR versions. Move to 1.0.0 when the API is stable.
Delete cluster and all resources:
make destroyImportant: Managed identity clusters require special destroy handling to ensure proper cleanup order. The destroy process will:
- Delete the cluster first
- Wait and verify the cluster is fully deleted (up to 10 minutes)
- Then delete remaining resources (managed identities, networks, etc.)
This prevents network resources from being destroyed while the cluster still exists.
Delete managed identity cluster and all resources:
make destroy-managed-identity.forceWhy a separate target? Destroy order matters (cluster before identities/RBAC). The script targets the AzAPI cluster resource first, then runs a full destroy; it also still recognizes a legacy ARM template resource in state if present.