VCF 3.9 – automate managing certificates for Cloud Foundation components using Ansible.

In VCF 3.9 we can manage certificates for all Cloud Foundation components, including configuring a certificate authority, generating and downloading CSRs, and installing them.

In this post i will show you how to configure Microsoft certificate authority and create and install certificates for the following components:

  • Platform Services Controllers
  • vCenter Server
  • NSX Manager
  • SDDC Managee
  • vRealize Log Insight
  • vRealize Operations

And of course we will use Ansible. Because there is no any module that could be used so we need to use API.

NOTE!!! Because it’s not possible to create and install certificates for all components simultaneously (some VCF limitation) we need to do that one by on using predefined list (with_items).

My vars looks like that:

# vars
vcfRestApiUrlCa: https://sddcManager_FQDN/v1/certificate-authorities
vcfRestApiUrlCheckCa: https://sddcManager_FQDN/v1/certificate-authorities/Microsoft
vcfRestApiUrlCsrs: https://sddcManager_FQDN/v1/domains/MGMT/csrs
vcfRestApiUrlCert: https://sddcManager_FQDN/v1/domains/MGMT/certificates
vcfRestApiUrlTaskStatus: https://sddcManager_FQDN/v1/tasks
vcfRestApiUrlFetchCertificates: https://sddcManager_FQDN/v1/domains/MGMT/resource-certificates
issuingCA: https://issuingCA_FQDN

#credentials
vcfUser: admin
vcfPassword: s0mEpAsSw0rD
  
# VCF components ("One among: ESXI, SDDC_MANAGER, VCENTER, PSC, NSX_MANAGER, NSX_CONTROLLER, NSX_EDGE, NSXT_MANAGER, NSXT_CONTROLLER, NSXT_EDGE, VRLI, VROPS, LCM_REPO, VRA, VRSLCM, DEPOT_USER, VXRAIL_MANAGER, AD, BACKUP")
vcfResources:
  resources:
    - fqdn: vcs01_FQDN
      type: VCENTER
    - fqdn: psc01_FQDN
      type: PSC
    - fqdn: psc02_FQDN
      type: PSC
    - fqdn: vrli_FQDN
      type: VRLI
    - fqdn: lcm_FQDN
      type: VRSLCM
    - fqdn: nsx_FQDN
      type: NSX_MANAGER
    - fqdn: sddcManager_FQDN
      type: SDDC_MANAGER
    - fqdn: vrops_FQDN
      type: VROPS

#VCF services that need to be restarted  
vcfServices:
  - commonsvcs
  - lcm
  - domainmanager
  - operationsmanager
  - sddc-manager-ui-app
  - solutionsmanager

Playbook:

#playbook
- name: VCF - certificates management
  hosts: sddcMnager
  gather_facts: false
  become: yes

  tasks:
    - name: "Running role: vcfCerts "
      include_role:
        name: manageVcfCertificates

And tasks: I created two tasks: createCerts.yml and restartVcfServices.yml

As I mentioned at the very beginning of this post, we will create and install certificates for all VCF components (defined in vars as ‘vcfResources’), so we need to use loop (with_items) in main.yml:

#main.yml
- name: Create and install cert for all VCF components
  include_tasks: createCerts.yml
  with_items:
  - "{{ vcfResources.resources }}"

- name: Restart VCF services
  include_tasks: restartVcfServices.yml

And tasks:

#createCerts.yml
- name: Check if Certificate Authority is configured.
  uri:
    url: "{{ vcfRestApiUrlCa }}"
    method: GET
    user: "{{ vcfUser }}"
    password: "{{ vcfPassword }}"
    validate_certs: no
    force_basic_auth: yes
    return_content: yes
    status_code:
      - 200
  register: checkCa
  delegate_to: localhost

- name: Certificate Authority configuration status
  debug:
   msg: "Microsoft Certificate Authority is not configured"
  when: (checkCa.json.elements | length == 0)

If CA is not yet configured, we can do that in that way:

- name: Create a certificate authority if not exists
  uri:
    url: "{{ vcfRestApiUrlCa }}"
    method: PUT
    user: "{{ vcfUser }}"
    password: "{{ vcfPassword }}"
    validate_certs: no
    headers:
      Content-Type: "application/json"
    force_basic_auth: yes
    return_content: yes
    body_format: json
    status_code:
      - 200
    body: 
      microsoftCertificateAuthoritySpec:
        secret: "{{ vcfPassword }}"
        serverUrl: "https://{{ issuingCA }}/certsrv"
        templateName: webserver
        username: "{{ temporaryCredentials.windowsUsername }}@{{ activeDirectory.domainName }}"
  register: createCa
  delegate_to: localhost
  when: (checkCa.json.elements | length == 0)


- name : Certificate Authority configuration status
  debug:
   msg: "Microsoft Certificate Authority https://issuingCA_FQDN/certsrv configured"
  when: (checkCa.json.elements | length > 0)

So now let’s create CSR first:

- name: "Generate CSR for {{ item.fqdn }}"
  uri:
    url: "{{ vcfRestApiUrlCsrs }}"
    method: PUT
    user: "{{ vcfUser }}"
    password: "{{ vcfPassword }}"
    validate_certs: no
    headers:
      Content-Type: "application/json"
    force_basic_auth: yes
    return_content: yes
    body_format: json
    status_code:
      - 200
      - 202
    body: 
      csrGenerationSpec:
        country: PL
        keyAlgorithm: RSA
        keySize: '2048'
        locality: PL
        organization: vconfig
        organizationUnit: vconfig
        state: PL
      resources:
      - fqdn: "{{ item.fqdn }}"
        type: "{{ item.type }}"
  register: generateCsr
  delegate_to: localhost

- name : "Get CSR creation task ID for {{ item.fqdn }}"
  debug:
   msg: "CSR task ID: {{generateCsr.json.id}}"
  when: generateCsr.status==202

- name: "Waiting for CSR creation - {{ item.fqdn }}"
  uri:
    url: "{{ vcfRestApiUrlTaskStatus }}/{{generateCsr.json.id}}"
    method: GET
    user: "{{ vcfUser }}"
    password: "{{ vcfPassword }}"
    validate_certs: no
    headers:
      Content-Type: "application/json"
    force_basic_auth: yes
    return_content: yes
    body_format: json
    status_code:
      - 200
#    - "{{ vcfResources.resources }}"
  register: CsrTaskStatus
#  when: csrValidationStatus.json.status=='SUCCESSFUL' 
  delegate_to: localhost
  until: (CsrTaskStatus.json.status == 'SUCCESSFUL')
  delay: 2
  retries: 100

- name : "CSR creation status - {{ item.fqdn }}"
  debug:
   msg: "CSR for {{ item.fqdn }} created"
  when: CsrTaskStatus.json.status=='SUCCESSFUL'

When CSR is ready we can generate certificate:

- name: "Generate certificate for {{ item.fqdn }}"
  uri:
    url: "{{ vcfRestApiUrlCert }}"
    method: PUT
    user: "{{ vcfUser }}"
    password: "{{ vcfPassword }}"
    validate_certs: no
    headers:
      Content-Type: "application/json"
    force_basic_auth: yes
    return_content: yes
    body_format: json
    status_code:
      - 200
      - 202
    body: 
      caType: Microsoft
      resources:
      - fqdn: "{{ item.fqdn }}"
        type: "{{ item.type }}"
#  with_items:
#    - "{{ vcfResources.resources }}"
  register: generateCert
#  when: csrValidationStatus.json.status=='SUCCESSFUL' 
  delegate_to: localhost


- name: "Waiting for certificate creation - {{ item.fqdn }}"
  uri:
    url: "{{ vcfRestApiUrlTaskStatus }}/{{generateCert.json.id}}"
    method: GET
    user: "{{ vcfUser }}"
    password: "{{ vcfPassword }}"
    validate_certs: no
    headers:
      Content-Type: "application/json"
    force_basic_auth: yes
    return_content: yes
    body_format: json
    status_code:
      - 200
  register: CertTaskStatus
  delegate_to: localhost
  until: (CertTaskStatus.json.status == 'SUCCESSFUL')
  delay: 10
  retries: 100

- name : "Certificate creation status - {{ item.fqdn }}" 
  debug:
   msg: "Certificate for {{ item.fqdn }} created"
  when: CertTaskStatus.json.status == 'SUCCESSFUL'

Certificate generated successfully so we let’s install it.

- name: "Install certificate for {{ item.fqdn }}"
  uri:
    url: "{{ vcfRestApiUrlCert }}"
    method: PATCH
    user: "{{ vcfUser }}"
    password: "{{ vcfPassword }}"
    validate_certs: no
    headers:
      Content-Type: "application/json"
    force_basic_auth: yes
    return_content: yes
    body_format: json
    status_code:
      - 200
      - 202
    body: 
      operationType: INSTALL
      resources:
      - fqdn: "{{ item.fqdn }}"
        type: "{{ item.type }}"
#  with_items:
#    - "{{ vcfResources.resources }}"
  register: installCert
  when: CertTaskStatus.json.status == 'SUCCESSFUL' 
  delegate_to: localhost

- name: "Waiting for certificate installation - {{ item.fqdn }}"
  uri:
    url: "{{ vcfRestApiUrlTaskStatus }}/{{installCert.json.id}}"
    method: GET
    user: "{{ vcfUser }}"
    password: "{{ vcfPassword }}"
    validate_certs: no
    headers:
      Content-Type: "application/json"
    force_basic_auth: yes
    return_content: yes
    body_format: json
    status_code:
      - 202
      - 200
  register: CertInstallationTaskStatus
  delegate_to: localhost
  until: (CertInstallationTaskStatus.json.status == 'SUCCESSFUL')
  delay: 120
  retries: 100
- name : Certificate installation status - {{ item.fqdn }}"
  debug:
    msg: "Certificate for {{ item.fqdn }} installed"
  when: CertInstallationTaskStatus.json.status == 'SUCCESSFUL'

And as you can see certificate for vRSLCM is installed. You can try to get to https://vRSLCM_FQDN and check if is installed correctly.

When all certificates are finally installed, the last step is to restart all SDDC Manager services after root certificate chain to the SDDC Manager keystore adding. Services are defined as ‘vcfServices’ in vars.

And the task looks like that:

- name: Restart SDDC Manager services after root certificate chain to the SDDC Manager keystore adding.
  service:
    name: "{{ item }}"
    state: restarted
  with_items: "{{ vcfServices }}"
  become: yes
  become_method: sudo
  become_user: root

That’s it. If you have any idea how to do that in much easier way i’ll be more than welcome to read about that.

Cheers!!