Best Practices: Configuring Static Network Interfaces on CentOS 7 Using Ansible Playbooks


4 views

When working with CentOS 7, we encounter several architectural changes from previous versions that impact network configuration:

  • NetworkManager as the default network configuration daemon
  • Consistent network device naming via udev
  • Systemd integration replacing traditional init scripts

The most maintainable method is to use Ansible's template module to manage interface configuration files while keeping NetworkManager operational. Here's why:

- Maintains consistency with CentOS 7's default architecture
- Allows for both GUI and CLI management when needed
- Provides better integration with modern networking features

Here's a complete Ansible playbook example that configures a static IP address:

---
- name: Configure static network interface
  hosts: centos_servers
  become: yes
  vars:
    interface_name: "{{ ansible_default_ipv4.interface }}"
    static_ip: 192.168.1.100
    netmask: 255.255.255.0
    gateway: 192.168.1.1
    dns_servers:
      - 8.8.8.8
      - 8.8.4.4

  tasks:
    - name: Create interface configuration file
      template:
        src: templates/ifcfg-static.j2
        dest: /etc/sysconfig/network-scripts/ifcfg-{{ interface_name }}
        owner: root
        group: root
        mode: 0644
      notify: restart network

    - name: Ensure NetworkManager is running
      service:
        name: NetworkManager
        state: started
        enabled: yes

  handlers:
    - name: restart network
      service:
        name: network
        state: restarted

Create this Jinja2 template at templates/ifcfg-static.j2:

DEVICE={{ interface_name }}
BOOTPROTO=static
IPADDR={{ static_ip }}
NETMASK={{ netmask }}
GATEWAY={{ gateway }}
DNS1={{ dns_servers[0] }}
DNS2={{ dns_servers[1] }}
ONBOOT=yes
NM_CONTROLLED=yes
TYPE=Ethernet

For environments where you prefer direct NetworkManager control:

- name: Configure static IP via nmcli
  command: |
    nmcli con add con-name static-{{ interface_name }} ifname {{ interface_name }} type ethernet \
    ip4 {{ static_ip }}/{{ netmask|replace('255.255.255.','') }} gw4 {{ gateway }}
  args:
    creates: /etc/sysconfig/network-scripts/ifcfg-static-{{ interface_name }}

For servers with multiple NICs, extend the playbook with interface-specific variables:

vars:
  network_interfaces:
    - name: enp0s3
      ip: 192.168.1.100
      gateway: 192.168.1.1
    - name: enp0s8
      ip: 10.0.0.100
      gateway: 10.0.0.1

Modern CentOS 7 systems present unique challenges for network configuration compared to their RHEL 6 predecessors. With NetworkManager enabled by default and predictable network interface naming, traditional approaches to static IP assignment need reconsideration. The key components affecting our configuration approach include:

  • NetworkManager service (default enabled)
  • systemd-networkd (alternative)
  • Consistent device naming (enpXsY format)
  • ifcfg files legacy location (/etc/sysconfig/network-scripts/)

Ansible offers several approaches to handle network configuration on CentOS 7:


# Main modules to consider:
- nmcli (for NetworkManager configurations)
- template (for ifcfg file generation)
- ini_file (for direct file modifications)
- lineinfile (for quick edits)
- systemd (for service management)

Here's a complete Ansible playbook example that handles static IP configuration while maintaining compatibility with NetworkManager:


- name: Configure static network interface
  hosts: centos_servers
  become: yes
  
  vars:
    interface_name: "{{ ansible_default_ipv4.interface }}"
    static_ip: 192.168.1.100
    netmask: 255.255.255.0
    gateway: 192.168.1.1
    dns_servers:
      - 8.8.8.8
      - 8.8.4.4

  tasks:
    - name: Ensure NetworkManager is running
      service:
        name: NetworkManager
        state: started
        enabled: yes

    - name: Create static connection profile
      nmcli:
        conn_name: "static-{{ interface_name }}"
        ifname: "{{ interface_name }}"
        type: ethernet
        ip4: "{{ static_ip }}/24"
        gw4: "{{ gateway }}"
        dns4: "{{ dns_servers | join(',') }}"
        state: present

    - name: Set the connection as default
      nmcli:
        conn_name: "static-{{ interface_name }}"
        autoconnect: yes
        state: up

For environments where NetworkManager is not desired, here's how to implement static configuration via ifcfg files:


- name: Configure network using ifcfg files
  template:
    src: templates/ifcfg-eth0.j2
    dest: /etc/sysconfig/network-scripts/ifcfg-{{ interface_name }}
    owner: root
    group: root
    mode: 0644
  notify: restart network

- name: Ensure NetworkManager is disabled
  service:
    name: NetworkManager
    state: stopped
    enabled: no

- name: Ensure network service is enabled
  service:
    name: network
    state: started
    enabled: yes

handlers:
  - name: restart network
    service:
      name: network
      state: restarted

DEVICE={{ interface_name }}
BOOTPROTO=static
IPADDR={{ static_ip }}
NETMASK={{ netmask }}
GATEWAY={{ gateway }}
DNS1={{ dns_servers[0] }}
DNS2={{ dns_servers[1] }}
ONBOOT=yes
TYPE=Ethernet

Always include validation steps in your playbook:


- name: Verify network configuration
  assert:
    that:
      - ansible_default_ipv4.address == static_ip
      - ansible_default_ipv4.gateway == gateway
    success_msg: "Network configuration verified successfully"
    fail_msg: "Network configuration verification failed"
  • Always test network changes in a staging environment first
  • Consider using host_vars for interface-specific configurations
  • Implement proper error handling for network changes
  • Document all network configuration standards
  • Use ansible_facts to make playbooks more dynamic