--- # Install Automation Orchestrator via its OpenShift OLM operator. # # Automation Orchestrator is bring-your-own-PostgreSQL: this role only creates the # OperatorGroup/Subscription and the AutomationOrchestrator CR — it references # existing K8s Secrets (keys: database/username/password, or host/port/database/ # username/password for CNPG's basic-auth secrets) rather than creating them. # Pair with the cloudnativepg_operator role (defaults match its secret names) or # point postgres_* variables at a customer-managed PostgreSQL instance. # # Modeled on https://github.com/ansible/aapctl (internal/install/automationorchestrator) # and https://github.com/automation-nexus/automation-orchestrator-operator. # With Manual install-plan approval (the default) this role approves the initial # InstallPlan itself; later upgrade InstallPlans are left for manual approval. # # All tasks are idempotent (kubernetes.core.k8s state: present). - name: Verify PostgreSQL host is set ansible.builtin.assert: that: - automation_orchestrator_operator_postgres_host | length > 0 fail_msg: >- automation_orchestrator_operator_postgres_host must be set. Automation Orchestrator is bring-your-own-PostgreSQL — either run the cloudnativepg_operator role first (its defaults match this role's) or point this at a customer-managed PostgreSQL instance. # ------------------------------------------------------------------ # Step 0: Mirror pre-release AAP 2.7 images (IDMS) — triggers an MCP rollout # ------------------------------------------------------------------ - name: Configure pre-release image mirrors ansible.builtin.include_tasks: image_mirrors.yml when: automation_orchestrator_operator_image_digest_mirrors | length > 0 # ------------------------------------------------------------------ # Step 1: Install Automation Orchestrator operator via OLM # ------------------------------------------------------------------ - name: Create Automation Orchestrator namespace kubernetes.core.k8s: state: present definition: apiVersion: v1 kind: Namespace metadata: name: "{{ automation_orchestrator_operator_operator_namespace }}" - name: Create Automation Orchestrator app namespace kubernetes.core.k8s: state: present definition: apiVersion: v1 kind: Namespace metadata: name: "{{ automation_orchestrator_operator_app_namespace }}" when: automation_orchestrator_operator_app_namespace != automation_orchestrator_operator_operator_namespace - name: Create CatalogSource for Automation Orchestrator kubernetes.core.k8s: state: present definition: apiVersion: operators.coreos.com/v1alpha1 kind: CatalogSource metadata: name: "{{ automation_orchestrator_operator_catalog_source_name }}" namespace: openshift-marketplace spec: sourceType: grpc image: "{{ automation_orchestrator_operator_catalog_image }}" displayName: Automation Orchestrator when: automation_orchestrator_operator_catalog_image | length > 0 # OLM allows only one OperatorGroup per namespace; reuse any existing one # (e.g. the auto-named group an OperatorHub install creates). - name: Look up existing OperatorGroups kubernetes.core.k8s_info: api_version: operators.coreos.com/v1 kind: OperatorGroup namespace: "{{ automation_orchestrator_operator_operator_namespace }}" register: __automation_orchestrator_operator_ogs - name: Create OperatorGroup for Automation Orchestrator kubernetes.core.k8s: state: present definition: apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: name: "{{ automation_orchestrator_operator_name }}" namespace: "{{ automation_orchestrator_operator_operator_namespace }}" spec: {} when: __automation_orchestrator_operator_ogs.resources | length == 0 - name: Subscribe to Automation Orchestrator operator kubernetes.core.k8s: state: present definition: apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: "{{ automation_orchestrator_operator_name }}" namespace: "{{ automation_orchestrator_operator_operator_namespace }}" spec: channel: "{{ automation_orchestrator_operator_channel }}" installPlanApproval: "{{ automation_orchestrator_operator_install_plan_approval }}" name: "{{ automation_orchestrator_operator_name }}" source: "{{ automation_orchestrator_operator_catalog_source_name }}" sourceNamespace: openshift-marketplace # ------------------------------------------------------------------ # Step 2: Approve the initial InstallPlan (if needed) and wait for the CSV # ------------------------------------------------------------------ # Once a CSV is installed, pending InstallPlans are upgrades — leave those for # manual approval rather than approving them on every run. - name: Wait for OLM to resolve the Subscription kubernetes.core.k8s_info: api_version: operators.coreos.com/v1alpha1 kind: Subscription namespace: "{{ automation_orchestrator_operator_operator_namespace }}" name: "{{ automation_orchestrator_operator_name }}" register: __automation_orchestrator_operator_sub until: >- __automation_orchestrator_operator_sub.resources | length > 0 and ((__automation_orchestrator_operator_sub.resources[0].status.installedCSV | default('')) | length > 0 or (__automation_orchestrator_operator_sub.resources[0].status.installPlanRef.name | default('')) | length > 0) retries: "{{ __automation_orchestrator_operator_wait_retries }}" delay: 10 - name: Resolve installed CSV name ansible.builtin.set_fact: __automation_orchestrator_operator_csv_name: >- {{ __automation_orchestrator_operator_sub.resources[0].status.installedCSV | default('') }} - name: Approve the initial InstallPlan when: __automation_orchestrator_operator_csv_name | length == 0 block: - name: Read the InstallPlan kubernetes.core.k8s_info: api_version: operators.coreos.com/v1alpha1 kind: InstallPlan namespace: "{{ automation_orchestrator_operator_operator_namespace }}" name: "{{ __automation_orchestrator_operator_sub.resources[0].status.installPlanRef.name }}" register: __automation_orchestrator_operator_installplan - name: Approve the InstallPlan kubernetes.core.k8s_json_patch: api_version: operators.coreos.com/v1alpha1 kind: InstallPlan namespace: "{{ automation_orchestrator_operator_operator_namespace }}" name: "{{ __automation_orchestrator_operator_sub.resources[0].status.installPlanRef.name }}" patch: - op: add path: /spec/approved value: true when: not (__automation_orchestrator_operator_installplan.resources[0].spec.approved | default(false)) - name: Resolve CSV name from the InstallPlan ansible.builtin.set_fact: __automation_orchestrator_operator_csv_name: >- {{ __automation_orchestrator_operator_installplan.resources[0].spec.clusterServiceVersionNames[0] }} - name: Wait for Automation Orchestrator CSV to succeed kubernetes.core.k8s_info: api_version: operators.coreos.com/v1alpha1 kind: ClusterServiceVersion namespace: "{{ automation_orchestrator_operator_operator_namespace }}" name: "{{ __automation_orchestrator_operator_csv_name }}" register: __automation_orchestrator_operator_csv until: >- __automation_orchestrator_operator_csv.resources | length > 0 and __automation_orchestrator_operator_csv.resources[0].status.phase | default('') == 'Succeeded' retries: "{{ __automation_orchestrator_operator_wait_retries }}" delay: 10 - name: Wait for AutomationOrchestrator CRD to be available kubernetes.core.k8s_info: api_version: apiextensions.k8s.io/v1 kind: CustomResourceDefinition name: automationorchestrators.aap.ansible.com register: __automation_orchestrator_operator_crd until: __automation_orchestrator_operator_crd.resources | length > 0 retries: "{{ __automation_orchestrator_operator_wait_retries }}" delay: 10 # ------------------------------------------------------------------ # Step 3: Deploy the AutomationOrchestrator instance # ------------------------------------------------------------------ - name: Build AutomationOrchestrator spec ansible.builtin.set_fact: __automation_orchestrator_operator_spec: >- {{ { 'imagePullSecrets': automation_orchestrator_operator_image_pull_secrets, 'postgres': { 'host': automation_orchestrator_operator_postgres_host, 'port': automation_orchestrator_operator_postgres_port, 'sslMode': automation_orchestrator_operator_postgres_ssl_mode, 'backendDatabase': {'secretRef': {'name': automation_orchestrator_operator_backend_db_secret_name}}, 'temporalDatabase': {'secretRef': {'name': automation_orchestrator_operator_temporal_db_secret_name}}, }, 'ingress': { 'type': automation_orchestrator_operator_ingress_type, 'annotations': automation_orchestrator_operator_ingress_annotations, }, 'backend': automation_orchestrator_operator_backend, 'ui': automation_orchestrator_operator_ui, 'worker': automation_orchestrator_operator_worker, 'backgroundWorker': automation_orchestrator_operator_background_worker, 'redis': automation_orchestrator_operator_redis, 'temporal': {'server': automation_orchestrator_operator_temporal_server}, } | combine( {'postgres': {'caCertSecretRef': {'name': automation_orchestrator_operator_postgres_ca_cert_secret}}} if automation_orchestrator_operator_postgres_ca_cert_secret else {}, recursive=True ) | combine( {'ingress': {'host': automation_orchestrator_operator_ingress_host}} if automation_orchestrator_operator_ingress_host else {}, recursive=True ) | combine( {'ingress': {'tlsSecretRef': {'name': automation_orchestrator_operator_ingress_tls_secret_name}}} if (automation_orchestrator_operator_ingress_type == 'Ingress' and automation_orchestrator_operator_ingress_tls_secret_name) else {}, recursive=True ) | combine(automation_orchestrator_operator_extra_spec, recursive=True) }} - name: Create AutomationOrchestrator instance kubernetes.core.k8s: state: present definition: apiVersion: aap.ansible.com/v1alpha1 kind: AutomationOrchestrator metadata: name: "{{ automation_orchestrator_operator_instance_name }}" namespace: "{{ automation_orchestrator_operator_app_namespace }}" spec: "{{ __automation_orchestrator_operator_spec }}" # ------------------------------------------------------------------ # Step 4: Wait for the instance to be ready # ------------------------------------------------------------------ - name: Wait for AutomationOrchestrator to be ready kubernetes.core.k8s_info: api_version: aap.ansible.com/v1alpha1 kind: AutomationOrchestrator namespace: "{{ automation_orchestrator_operator_app_namespace }}" name: "{{ automation_orchestrator_operator_instance_name }}" register: __automation_orchestrator_operator_status ignore_errors: true until: >- __automation_orchestrator_operator_status.resources is defined and __automation_orchestrator_operator_status.resources | length > 0 and (__automation_orchestrator_operator_status.resources[0].status.conditions | default([]) | selectattr('type', '==', 'Ready') | selectattr('status', '==', 'True') | list | length > 0) retries: "{{ __automation_orchestrator_operator_wait_retries }}" delay: 10 # ------------------------------------------------------------------ # Step 5: Display summary # ------------------------------------------------------------------ - name: Display Automation Orchestrator deployment summary ansible.builtin.debug: msg: - "Automation Orchestrator deployment complete!" - " Namespace : {{ automation_orchestrator_operator_app_namespace }}" - " Instance : {{ automation_orchestrator_operator_instance_name }}" - " Ingress type: {{ automation_orchestrator_operator_ingress_type }}" - "" - "Find the UI hostname with: oc get routes -n {{ automation_orchestrator_operator_app_namespace }}" - "The initial admin password is auto-generated unless spec.secrets.initialAdminPasswordSecretRef is set:" - " oc get secret {{ automation_orchestrator_operator_instance_name }}-initial-admin-password -n {{ automation_orchestrator_operator_app_namespace }} -o jsonpath='{.data.password}' | base64 -d"