Ansible OpenShift Automation: Deploy, Manage, and Scale Kubernetes Workloads
By Luca Berton · Published 2024-01-01 · Category: installation
Complete guide to automating Red Hat OpenShift with Ansible. Learn cluster provisioning, application deployment, route management, operators, CI/CD pipelines.
Red Hat OpenShift is an enterprise Kubernetes platform that adds developer workflows, built-in CI/CD, and operational tools on top of Kubernetes. Ansible provides powerful automation for OpenShift — from cluster provisioning to application deployment, route management, and day-2 operations. This guide covers production-ready patterns using the kubernetes.core and redhat.openshift collections.
Prerequisites
# Install required collections
ansible-galaxy collection install kubernetes.core
ansible-galaxy collection install redhat.openshift
# Install Python dependencies
pip install kubernetes openshift PyYAML
# Verify OpenShift CLI
oc version
Authentication Setup
# group_vars/all.yml
openshift_host: "https://api.ocp.example.com:6443"
openshift_api_key: "{{ vault_openshift_token }}"
openshift_validate_certs: true
# Or use kubeconfig
openshift_kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}"
See also: Ansible on OpenShift 4.18 Automation Complete Guide
Project and Namespace Management
---
- name: Manage OpenShift projects
hosts: localhost
module_defaults:
group/kubernetes.core.k8s:
host: "{{ openshift_host }}"
api_key: "{{ openshift_api_key }}"
validate_certs: "{{ openshift_validate_certs }}"
tasks:
- name: Create project for application
redhat.openshift.openshift_project:
name: myapp-production
display_name: "My Application - Production"
description: "Production environment for MyApp"
state: present
- name: Set resource quotas
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: myapp-production
spec:
hard:
requests.cpu: "8"
requests.memory: "16Gi"
limits.cpu: "16"
limits.memory: "32Gi"
pods: "50"
- name: Set limit ranges
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: myapp-production
spec:
limits:
- type: Container
default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
- name: Configure network policy
kubernetes.core.k8s:
state: present
definition:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-namespace
namespace: myapp-production
spec:
podSelector: {}
ingress:
- from:
- podSelector: {}
policyTypes:
- Ingress
Application Deployment
Deploy a Complete Application Stack
---
- name: Deploy application to OpenShift
hosts: localhost
vars:
app_name: myapp
app_namespace: myapp-production
app_image: "registry.example.com/myapp:{{ app_version | default('latest') }}"
app_replicas: 3
module_defaults:
group/kubernetes.core.k8s:
host: "{{ openshift_host }}"
api_key: "{{ openshift_api_key }}"
validate_certs: "{{ openshift_validate_certs }}"
tasks:
- name: Create ConfigMap
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: ConfigMap
metadata:
name: "{{ app_name }}-config"
namespace: "{{ app_namespace }}"
data:
APP_ENV: production
LOG_LEVEL: warn
DB_HOST: postgresql.{{ app_namespace }}.svc.cluster.local
DB_PORT: "5432"
- name: Create Secret
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Secret
metadata:
name: "{{ app_name }}-secrets"
namespace: "{{ app_namespace }}"
type: Opaque
stringData:
DB_PASSWORD: "{{ vault_db_password }}"
API_KEY: "{{ vault_api_key }}"
- name: Deploy application
kubernetes.core.k8s:
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: "{{ app_name }}"
namespace: "{{ app_namespace }}"
labels:
app: "{{ app_name }}"
version: "{{ app_version | default('latest') }}"
spec:
replicas: "{{ app_replicas }}"
selector:
matchLabels:
app: "{{ app_name }}"
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: "{{ app_name }}"
spec:
containers:
- name: "{{ app_name }}"
image: "{{ app_image }}"
ports:
- containerPort: 8080
name: http
envFrom:
- configMapRef:
name: "{{ app_name }}-config"
- secretRef:
name: "{{ app_name }}-secrets"
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
- name: Create Service
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Service
metadata:
name: "{{ app_name }}"
namespace: "{{ app_namespace }}"
spec:
selector:
app: "{{ app_name }}"
ports:
- port: 8080
targetPort: http
name: http
- name: Create Route (OpenShift-specific)
redhat.openshift.openshift_route:
name: "{{ app_name }}"
namespace: "{{ app_namespace }}"
service: "{{ app_name }}"
tls:
termination: edge
insecure_policy: Redirect
host: "{{ app_name }}.apps.ocp.example.com"
state: present
- name: Wait for deployment rollout
kubernetes.core.k8s_info:
api_version: apps/v1
kind: Deployment
name: "{{ app_name }}"
namespace: "{{ app_namespace }}"
register: deployment_status
until:
- deployment_status.resources[0].status.readyReplicas is defined
- deployment_status.resources[0].status.readyReplicas == app_replicas
retries: 30
delay: 10
- name: Display route URL
ansible.builtin.debug:
msg: "Application deployed at https://{{ app_name }}.apps.ocp.example.com"
See also: Install Minikube with Ansible Role on All Hosts
BuildConfig and ImageStream
---
- name: Configure OpenShift builds
hosts: localhost
module_defaults:
group/kubernetes.core.k8s:
host: "{{ openshift_host }}"
api_key: "{{ openshift_api_key }}"
tasks:
- name: Create ImageStream
kubernetes.core.k8s:
state: present
definition:
apiVersion: image.openshift.io/v1
kind: ImageStream
metadata:
name: myapp
namespace: myapp-production
- name: Create BuildConfig (Source-to-Image)
kubernetes.core.k8s:
state: present
definition:
apiVersion: build.openshift.io/v1
kind: BuildConfig
metadata:
name: myapp-build
namespace: myapp-production
spec:
source:
type: Git
git:
uri: "https://github.com/myorg/myapp.git"
ref: main
strategy:
type: Source
sourceStrategy:
from:
kind: ImageStreamTag
namespace: openshift
name: "python:3.11-ubi9"
output:
to:
kind: ImageStreamTag
name: "myapp:latest"
triggers:
- type: ConfigChange
- type: GitHub
github:
secret: "{{ vault_github_webhook_secret }}"
- name: Start a build
redhat.openshift.openshift_build:
name: myapp-build
namespace: myapp-production
build_type: Source
wait: true
wait_timeout: 600
Horizontal Pod Autoscaler
- name: Configure autoscaling
kubernetes.core.k8s:
state: present
definition:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
namespace: myapp-production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
See also: Ansible vs Kubernetes: Key Differences & When to Use Each (2026 Guide)
Operator Management
---
- name: Install and manage operators
hosts: localhost
module_defaults:
group/kubernetes.core.k8s:
host: "{{ openshift_host }}"
api_key: "{{ openshift_api_key }}"
tasks:
- name: Create operator subscription
kubernetes.core.k8s:
state: present
definition:
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: elasticsearch-operator
namespace: openshift-operators
spec:
channel: stable
name: elasticsearch-operator
source: redhat-operators
sourceNamespace: openshift-marketplace
installPlanApproval: Automatic
- name: Wait for operator to install
kubernetes.core.k8s_info:
api_version: operators.coreos.com/v1alpha1
kind: ClusterServiceVersion
namespace: openshift-operators
label_selectors:
- "operators.coreos.com/elasticsearch-operator.openshift-operators"
register: csv_status
until:
- csv_status.resources | length > 0
- csv_status.resources[0].status.phase == "Succeeded"
retries: 30
delay: 10
Day-2 Operations
Certificate Rotation
---
- name: Rotate application certificates
hosts: localhost
tasks:
- name: Update TLS secret with new certificate
kubernetes.core.k8s:
state: present
force: true
definition:
apiVersion: v1
kind: Secret
metadata:
name: myapp-tls
namespace: myapp-production
type: kubernetes.io/tls
data:
tls.crt: "{{ lookup('file', 'certs/new-cert.pem') | b64encode }}"
tls.key: "{{ lookup('file', 'certs/new-key.pem') | b64encode }}"
- name: Trigger rolling restart to pick up new certs
kubernetes.core.k8s:
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: myapp-production
spec:
template:
metadata:
annotations:
kubectl.kubernetes.io/restartedAt: "{{ ansible_date_time.iso8601 }}"
Backup and Restore
---
- name: Backup OpenShift resources
hosts: localhost
vars:
backup_dir: "/backups/openshift/{{ ansible_date_time.date }}"
namespaces:
- myapp-production
- myapp-staging
tasks:
- name: Create backup directory
ansible.builtin.file:
path: "{{ backup_dir }}"
state: directory
mode: "0750"
- name: Export namespace resources
kubernetes.core.k8s_info:
api_version: "{{ item.1 }}"
kind: "{{ item.0 }}"
namespace: "{{ ns }}"
loop: "{{ ['Deployment', 'Service', 'ConfigMap', 'Secret', 'Route'] | product(['v1', 'apps/v1', 'route.openshift.io/v1']) | list }}"
loop_control:
label: "{{ item.0 }}"
vars:
ns: "{{ namespaces[0] }}"
register: resources
failed_when: false
- name: Save resources to files
ansible.builtin.copy:
content: "{{ item.resources | to_nice_yaml }}"
dest: "{{ backup_dir }}/{{ item.item.0 | lower }}.yaml"
loop: "{{ resources.results | selectattr('resources', 'defined') | list }}"
loop_control:
label: "{{ item.item.0 }}"
when: item.resources | length > 0
Monitoring and Health Checks
---
- name: OpenShift cluster health check
hosts: localhost
tasks:
- name: Check node status
kubernetes.core.k8s_info:
api_version: v1
kind: Node
register: nodes
- name: Report unhealthy nodes
ansible.builtin.debug:
msg: "WARNING: Node {{ item.metadata.name }} is NotReady"
loop: "{{ nodes.resources }}"
loop_control:
label: "{{ item.metadata.name }}"
when: >-
item.status.conditions
| selectattr('type', 'equalto', 'Ready')
| map(attribute='status')
| first != 'True'
- name: Check cluster operators
kubernetes.core.k8s_info:
api_version: config.openshift.io/v1
kind: ClusterOperator
register: cluster_operators
- name: Report degraded operators
ansible.builtin.debug:
msg: "DEGRADED: {{ item.metadata.name }}"
loop: "{{ cluster_operators.resources }}"
loop_control:
label: "{{ item.metadata.name }}"
when: >-
item.status.conditions
| selectattr('type', 'equalto', 'Degraded')
| map(attribute='status')
| first == 'True'
Frequently Asked Questions
How do I authenticate Ansible with OpenShift?
Use an API token (oc whoami -t), a kubeconfig file, or a service account token. For automation, create a dedicated service account with appropriate RBAC roles and use its token.
What is the difference between kubernetes.core and redhat.openshift collections?
kubernetes.core provides modules for standard Kubernetes resources (deployments, services, configmaps). redhat.openshift adds OpenShift-specific modules for routes, builds, projects, and OpenShift-specific resources. Use both together.
Can I manage multiple OpenShift clusters from one Ansible playbook?
Yes. Use different connection parameters per task or leverage inventory groups with cluster-specific variables. The kubeconfig parameter or host/api_key can be set per-task.
How do I handle OpenShift upgrades with Ansible?
OpenShift cluster upgrades are managed through the Cluster Version Operator (CVO). Use Ansible to trigger upgrades via the kubernetes.core.k8s module to patch the ClusterVersion resource, then monitor the upgrade progress.
What RBAC permissions does Ansible need in OpenShift?
At minimum, create a service account with edit or admin role in target namespaces. For cluster-wide operations (nodes, operators, namespaces), use cluster-admin or custom ClusterRoles.
Related Articles
• Ansible Kubernetes Automation Enterprise Guide • Ansible Container Security Automation • Ansible CI CD Pipeline Integration • Ansible Execution EnvironmentsConclusion
Ansible and OpenShift together create a powerful enterprise platform for cloud-native automation. From provisioning projects and deploying applications to managing operators and running day-2 operations, Ansible brings infrastructure-as-code consistency to your OpenShift environment. Use the patterns in this guide as building blocks for your own automation — start with deployment workflows, add health checks, then expand to operator management and cluster-wide automation.
Category: installation