Skip to content

Managed OAuth session

Use case: an automation identity authenticates with OAuth instead of a long-lived personal token. A caller-managed access token needs only three fields; opting into managed refresh hands the role a durable controller-side store that survives token rotation — a real credential mutation, since GitLab invalidates the previous pair on every exchange. The example also shows the focused gitlab_credential_check and gitlab_oauth_session tasks, and an explicitly anonymous read of a public resource.

ansible-playbook -i localhost, examples/oauth-managed-session.yml

Source: examples/oauth-managed-session.yml

---
- name: Reconcile GitLab settings with a managed OAuth session
  hosts: localhost
  gather_facts: false
  connection: local
  vars:
    # A caller-managed access token needs only these three fields. Managed
    # refresh is opt-in because rotating a refresh token is a real credential
    # mutation: GitLab invalidates the previous pair on every exchange.
    gitlab_settings_target:
      url: https://gitlab.example.com
      auth:
        type: oauth2
        access_token: "{{ vault_gitlab_oauth_access_token }}"
        refresh_token: "{{ vault_gitlab_oauth_refresh_token }}"
        client_id: "{{ vault_gitlab_oauth_client_id }}"
        # Omit client_secret for a public PKCE client. An empty value is a
        # missing-secret error, never an implicit downgrade.
        client_secret: "{{ vault_gitlab_oauth_client_secret }}"
        redirect_uri: https://automation.example.com/oauth/callback
        refresh:
          enabled: true
          before_expiry: 300
          store:
            type: controller_file
            path: /secure/ansible/oauth/gitlab-primary.json
  tasks:
    # Optional. One request verifies the credential can do the work below:
    # that it is active, is the kind you declared, was issued by the expected
    # application, has not expired, and carries scopes the operations accept.
    # List every resource the play touches: their requirements are resolved
    # locally, so the whole set is compared against a single response. Running
    # it here keeps that cost at one request no matter how many groups the loop
    # then reconciles.
    - name: Verify the credential before converging anything
      primetheus.gitlab_settings.gitlab_credential_check:
        target: "{{ gitlab_settings_target }}"
        resources: [group]
        write: true

    # Optional. Ordinary tasks refresh transparently in a normal run; this is
    # only needed to establish or repair the session deliberately, or to make
    # a session usable by a later --check run.
    - name: Ensure the OAuth session is valid
      primetheus.gitlab_settings.gitlab_oauth_session:
        target: "{{ gitlab_settings_target }}"
      check_mode: false
      register: gitlab_oauth

    - name: Report safe session state
      ansible.builtin.debug:
        msg: >-
          session {{ 'maintained' if gitlab_oauth['changed'] else 'already current' }}
          ({{ gitlab_oauth['authentication']['refresh_reason'] }}),
          expires {{ gitlab_oauth['authentication']['expires_at'] | default('unknown') }}

    # Neither task below knows whether OAuth was refreshed, or whether the
    # selected settings backend is REST or GraphQL.
    - name: Discover all groups
      primetheus.gitlab_settings.gitlab_settings_discover:
        target: "{{ gitlab_settings_target }}"
        resource: group
        scope: all
      register: discovered_groups

    - name: Enable Duo for each group
      primetheus.gitlab_settings.gitlab_settings:
        target: "{{ gitlab_settings_target }}"
        resource: group
        identifier: "{{ item['identifier'] }}"
        settings:
          duo:
            duo_features_enabled: true
      loop: "{{ discovered_groups['resources'] }}"
      loop_control:
        label: "{{ item['identifier'] }}"

- name: Read a public GitLab resource without any credential
  hosts: localhost
  gather_facts: false
  connection: local
  tasks:
    # Anonymous access is explicit and is accepted only for operations GitLab
    # documents as publicly readable. It is never a fallback after a failure.
    # GitLab requires a credential to read instance metadata, so an anonymous
    # target declares the compatibility profile instead of probing for it.
    - name: Discover public groups on GitLab.com
      primetheus.gitlab_settings.gitlab_settings_discover:
        target:
          url: https://gitlab.com
          auth:
            type: anonymous
          version: 19.2.0-ee
        resource: group
        scope: all
        limit: 20
      register: public_groups

    - name: Report how many public groups were visible
      ansible.builtin.debug:
        msg: "{{ public_groups['resources'] | length }} public groups discovered"