Idempotent Ansible Solution: Generating and Setting System Locale (en_US.UTF-8)


3 views

html

When provisioning servers across different environments, inconsistent locale settings can cause unexpected behavior in applications. The en_US.UTF-8 locale is particularly important as it's the de facto standard for many internationalized applications.

Here's a production-tested solution that handles both locale generation and system-wide configuration:

- name: Ensure required locale is available
  become: yes
  locale_gen:
    name: "{{ locale_name | default('en_US.UTF-8') }}"
    state: present

- name: Configure system-wide locale settings
  become: yes
  block:
    - name: Check current locale configuration
      command: localectl status
      register: current_locale
      changed_when: false

    - name: Set system locale if needed
      command: localectl set-locale "LANG={{ locale_name | default('en_US.UTF-8') }}"
      when: >
        locale_name | default('en_US.UTF-8') not in current_locale.stdout

The solution combines Ansible's locale_gen module (for generating locales) with direct localectl commands (for system configuration). This provides several advantages:

  • True idempotency through explicit condition checks
  • Support for most modern Linux distributions
  • Clear separation between locale generation and system configuration

For environments requiring multiple locale settings, extend the solution with this pattern:

- name: Configure additional locale variables
  become: yes
  copy:
    dest: /etc/default/locale
    content: |
      LANG={{ primary_locale }}
      LC_COLLATE={{ collate_locale }}
      LC_CTYPE={{ ctype_locale }}
    validate: 'localectl set-locale --file %s'

Always include validation tasks in your playbook:

- name: Verify locale configuration
  command: locale
  register: locale_output
  changed_when: false

- name: Debug locale settings
  debug:
    msg: "Current locale settings:\n{{ locale_output.stdout }}"

System locales define language, character encoding, and regional settings for applications. The UTF-8 encoded locales (like en_US.UTF-8) have become the standard for modern systems. When automating server configuration with Ansible, we need to ensure both:

  • The desired locale is generated (available in the system)
  • The locale is set as default system-wide

Here's a robust approach that handles both requirements idempotently:

- name: Check if locale exists
  command: locale -a | grep "{{ locale_name }}"
  register: locale_check
  ignore_errors: yes
  changed_when: false

- name: Generate locale if missing
  become: yes
  command: locale-gen {{ locale_name }}
  when: locale_check.rc != 0

- name: Update default locale
  become: yes
  command: update-locale LANG={{ locale_name }} LC_ALL={{ locale_name }}
  register: locale_update
  changed_when: "'No changes' not in locale_update.stdout"

For production use, consider this enhanced version with variables and error handling:

---
# vars/main.yml
default_locale: en_US.UTF-8
supported_locales:
  - en_US.UTF-8
  - fr_FR.UTF-8

# tasks/main.yml
- name: Install locale packages
  become: yes
  apt:
    name: "{{ item }}"
    state: present
  loop:
    - locales
    - language-pack-en

- name: Ensure locales are generated
  become: yes
  command: locale-gen {{ item }}
  loop: "{{ supported_locales }}"
  when: item not in locale_check.stdout

- name: Set system default locale
  become: yes
  copy:
    dest: /etc/default/locale
    content: |
      LANG={{ default_locale }}
      LC_ALL={{ default_locale }}
  notify: Refresh locale

The solution needs adjustment for RHEL/CentOS systems:

- name: Install locales package (RHEL)
  become: yes
  yum:
    name: glibc-common
    state: present

- name: Generate locale (RHEL)
  become: yes
  command: localedef -c -i en_US -f UTF-8 en_US.UTF-8

Always include verification steps in your playbook:

- name: Verify current locale settings
  command: locale
  register: locale_status
  changed_when: false

- name: Debug locale settings
  debug:
    msg: "{{ locale_status.stdout_lines }}"