AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

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: How to Upgrade Kubelet in Kubernetes: A Complete 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.

Conclusion

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.

Related reading: Ansible-driven Kubernetes operations covers this in real-world detail.

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home