Module 19 Configuration Management 5 labs

Configuration Management: Ansible, PowerShell DSC, cloud-init

Quản lý cấu hình server, package, service, file, user và hardening theo hướng idempotent — từ Ansible playbook/role, PowerShell DSC trên Windows đến cloud-init bootstrap VM trên cloud.

Công cụ thực hành ansible, ansible-lint, pwsh, VS Code, Git
Nền tảng CLI, VS Code, Git, PowerShell, Windows Terminal
Thời điểm phát hành 23/05/2026
Ngày biên soạn 23/05/2026
Người biên soạn Trần Văn Hòa — Microsoft Certified Trainer (MCT)

Mục tiêu học tập

1. Lý thuyết cốt lõi

1.1. Configuration Management là gì?

Configuration Management (CM) là thực hành mô tả trạng thái mong muốn của hệ thống bằng code và để công cụ tự đồng bộ hệ thống thực tế về trạng thái đó — thay vì SSH vào từng server thực hiện thủ công. Nguyên tắc cốt lõi là idempotency: chạy playbook/config 1 lần hay 10 lần đều cho ra cùng kết quả, không phát sinh side effect. Theo Infrastructure as Code, 3rd Ed. (Kief Morris), CM là một trong bốn thực hành nền tảng của IaC, bên cạnh provisioning, immutable infrastructure và pipeline-driven changes.

Push vs Pull model

  • Push (Ansible, SaltStack SSH): control node chủ động đẩy config tới managed node. Đơn giản, không cần agent.
  • Pull (Puppet, Chef, DSC Pull): managed node định kỳ pull config từ server. Phù hợp fleet lớn, self-healing.
  • Hybrid (PowerShell DSC): hỗ trợ cả push (Start-DscConfiguration) và pull (LCM pull server/Azure Automation).

1.2. Ansible — kiến trúc và thành phần

Ansible là agentless CM tool dùng SSH/WinRM. Các thành phần chính:

Thành phầnMô tả
InventoryDanh sách host (static file hoặc dynamic inventory script/plugin)
PlaybookYAML file mô tả danh sách play, mỗi play gán role/task cho host group
RoleĐơn vị tái sử dụng: tasks, handlers, templates, vars, defaults, files
ModuleĐơn vị thực thi (apt, yum, service, file, user, copy, template…)
HandlerTask chỉ chạy khi được notify (ví dụ: restart nginx sau khi config thay đổi)
VaultMã hóa secrets trong playbook/var file (ansible-vault encrypt)

Thứ tự ưu tiên biến (thấp → cao): role defaults → inventory vars → playbook vars → extra-vars (-e). Dùng Jinja2 template ({{ variable }}) trong file config và template.

1.3. PowerShell DSC (Desired State Configuration)

DSC là nền tảng CM tích hợp sẵn trong Windows PowerShell và PowerShell 7+. Dùng cú pháp khai báo Configuration block mô tả trạng thái mong muốn, sau đó compile thành MOF (Managed Object Format) và áp dụng qua LCM (Local Configuration Manager). Key concepts:

1.4. cloud-init — bootstrap VM lần đầu

cloud-init là tiêu chuẩn de-facto để tự động hóa cấu hình VM ngay lần boot đầu tiên trên hầu hết cloud provider (AWS, Azure, GCP, OpenStack). User-data được inject qua metadata service; cloud-init xử lý theo thứ tự: network config → per-instance → per-boot → per-always. Các module phổ biến:

cloud-init thường dùng kết hợp với Terraform/Bicep: Terraform provision VM và truyền cloud-init user-data qua custom_data hoặc user_data. Log debug tại /var/log/cloud-init-output.log.

1.5. So sánh công cụ

Tiêu chíAnsiblePowerShell DSCcloud-init
OS targetLinux + WindowsWindows (chủ yếu)Linux (Ubuntu/RHEL/etc.)
AgentKhông (SSH/WinRM)LCM tích hợp sẵnDaemon trên image
Thời điểmOn-demand / scheduledOn-demand / pull intervalFirst boot only
Điểm mạnhLinh hoạt, ecosystem lớnTích hợp Windows/AzureNhanh, cloud-native

2. Thực hành (Labs)

LAB-091

Ansible playbook cài đặt và cấu hình Nginx (idempotent)

ansible · CLI

🎯 Mục tiêu: Viết playbook YAML idempotent cài Nginx, deploy file cấu hình từ template, bật service — chạy nhiều lần không gây lỗi.

🧰 Công cụ / nền tảng: ansible (pip install ansible), SSH key, Linux target (Ubuntu 22.04 hoặc WSL2), VS Code.

📦 Chuẩn bị: Cài Ansible trên control node; chuẩn bị SSH key và user có sudo trên managed node (có thể dùng localhost với connection: local).

▶️ Các bước (CLI):

# 1. Cài Ansible
pip install ansible

# 2. Tạo cấu trúc project
mkdir ansible-nginx && cd ansible-nginx

# 3. inventory.ini
cat > inventory.ini << 'EOF'
[webservers]
web1 ansible_host=127.0.0.1 ansible_connection=local ansible_user=root
EOF

# 4. Template Nginx config: templates/nginx.conf.j2
mkdir -p templates
cat > templates/nginx.conf.j2 << 'EOF'
server {
    listen {{ nginx_port | default(80) }};
    server_name {{ server_name | default('_') }};
    root /var/www/html;
    index index.html;
    location / { try_files $uri $uri/ =404; }
}
EOF

# 5. Playbook
cat > site.yml << 'EOF'
---
- name: Configure Nginx webserver
  hosts: webservers
  become: true
  vars:
    nginx_port: 80
    server_name: "lab.local"

  tasks:
    - name: Install Nginx
      ansible.builtin.apt:
        name: nginx
        state: present
        update_cache: true

    - name: Deploy Nginx config from template
      ansible.builtin.template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/sites-available/lab.conf
        owner: root
        group: root
        mode: '0644'
      notify: Reload Nginx

    - name: Enable site (symlink)
      ansible.builtin.file:
        src: /etc/nginx/sites-available/lab.conf
        dest: /etc/nginx/sites-enabled/lab.conf
        state: link

    - name: Ensure Nginx is started and enabled
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true

    - name: Create default index page
      ansible.builtin.copy:
        content: "

Deployed by Ansible on {{ inventory_hostname }}

" dest: /var/www/html/index.html mode: '0644' handlers: - name: Reload Nginx ansible.builtin.service: name: nginx state: reloaded EOF # 6. Chạy playbook lần 1 ansible-playbook -i inventory.ini site.yml -v # 7. Chạy lại lần 2 (kiểm tra idempotency) ansible-playbook -i inventory.ini site.yml # 8. Kiểm tra curl -s http://localhost/ | grep Ansible

✅ Kết quả mong đợi: Lần 1: output có changed ở các task install/config. Lần 2: tất cả task đều ok (0 changed) — idempotency đạt. curl http://localhost/ trả về HTML chứa "Deployed by Ansible".

🧹 Cleanup: ansible-playbook -i inventory.ini site.yml -e "state=absent" hoặc apt remove nginx -y.

LAB-092

Ansible Role chuẩn: package + service + file + user

ansible-galaxy · ansible-lint

🎯 Mục tiêu: Tạo Ansible Role đầy đủ cấu trúc để deploy một app node.js: cài runtime, tạo user chuyên dụng, deploy service file systemd, quản lý log.

🧰 Công cụ / nền tảng: ansible, ansible-lint, ansible-galaxy, VS Code, Git.

📦 Chuẩn bị: Môi trường từ LAB-091; cài ansible-lint: pip install ansible-lint.

▶️ Các bước:

# 1. Tạo role scaffold
ansible-galaxy role init roles/nodeapp
# Cấu trúc tạo ra:
# roles/nodeapp/{tasks,handlers,templates,files,vars,defaults,meta}/

# 2. defaults/main.yml — giá trị mặc định (override được)
cat > roles/nodeapp/defaults/main.yml << 'EOF'
---
node_version: "20"
app_user: nodeapp
app_dir: /opt/nodeapp
app_port: 3000
service_name: nodeapp
EOF

# 3. tasks/main.yml
cat > roles/nodeapp/tasks/main.yml << 'EOF'
---
- name: Install Node.js repository
  ansible.builtin.shell: |
    curl -fsSL https://deb.nodesource.com/setup_{{ node_version }}.x | bash -
  args:
    creates: /etc/apt/sources.list.d/nodesource.list

- name: Install Node.js
  ansible.builtin.apt:
    name: nodejs
    state: present

- name: Create app user
  ansible.builtin.user:
    name: "{{ app_user }}"
    system: true
    shell: /usr/sbin/nologin
    home: "{{ app_dir }}"
    create_home: true

- name: Create app directory
  ansible.builtin.file:
    path: "{{ app_dir }}"
    owner: "{{ app_user }}"
    group: "{{ app_user }}"
    mode: '0750'
    state: directory

- name: Deploy sample app
  ansible.builtin.copy:
    content: |
      const http = require('http');
      http.createServer((req, res) => {
        res.end('Hello from Node.js app on port {{ app_port }}\n');
      }).listen({{ app_port }});
    dest: "{{ app_dir }}/app.js"
    owner: "{{ app_user }}"
    mode: '0640'
  notify: Restart nodeapp

- name: Deploy systemd service
  ansible.builtin.template:
    src: nodeapp.service.j2
    dest: /etc/systemd/system/{{ service_name }}.service
    mode: '0644'
  notify:
    - Reload systemd
    - Restart nodeapp

- name: Ensure service is started and enabled
  ansible.builtin.service:
    name: "{{ service_name }}"
    state: started
    enabled: true
EOF

# 4. templates/nodeapp.service.j2
cat > roles/nodeapp/templates/nodeapp.service.j2 << 'EOF'
[Unit]
Description=Node.js App - {{ service_name }}
After=network.target

[Service]
User={{ app_user }}
WorkingDirectory={{ app_dir }}
ExecStart=/usr/bin/node {{ app_dir }}/app.js
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF

# 5. handlers/main.yml
cat > roles/nodeapp/handlers/main.yml << 'EOF'
---
- name: Reload systemd
  ansible.builtin.systemd:
    daemon_reload: true

- name: Restart nodeapp
  ansible.builtin.service:
    name: "{{ service_name }}"
    state: restarted
EOF

# 6. Playbook gọi role
cat > deploy-app.yml << 'EOF'
---
- name: Deploy Node.js application
  hosts: webservers
  become: true
  roles:
    - role: nodeapp
      vars:
        app_port: 3000
        node_version: "20"
EOF

# 7. Lint trước khi chạy
ansible-lint deploy-app.yml

# 8. Deploy
ansible-playbook -i inventory.ini deploy-app.yml

# 9. Verify
curl http://localhost:3000/

✅ Kết quả mong đợi: ansible-lint không có lỗi critical. Service nodeapp active. curl http://localhost:3000/ trả về "Hello from Node.js app". systemctl status nodeapp cho thấy active (running).

🧹 Cleanup: systemctl stop nodeapp && systemctl disable nodeapp, xóa /etc/systemd/system/nodeapp.service, userdel -r nodeapp.

LAB-093

PowerShell DSC cấu hình Windows Server

PowerShell · DSC · Windows

🎯 Mục tiêu: Dùng PowerShell DSC cài IIS, tạo website, tạo local user, cấu hình Windows Firewall và enforce trạng thái — kiểm tra drift bằng Test-DscConfiguration.

🧰 Công cụ / nền tảng: Windows Server 2022 hoặc Windows 10/11 với PowerShell 5.1+, VS Code + PowerShell extension.

📦 Chuẩn bị: Chạy PowerShell với quyền Administrator. Đặt execution policy: Set-ExecutionPolicy RemoteSigned -Scope LocalMachine.

▶️ Các bước (PowerShell):

# 1. Cài module DSC resource cần thiết
Install-Module -Name PSDscResources -Force -Scope AllUsers
Install-Module -Name xWebAdministration -Force -Scope AllUsers

# 2. Tạo Configuration script: WebServerConfig.ps1
$configScript = @'
Configuration WebServerBaseline {
    Import-DscResource -ModuleName PSDscResources
    Import-DscResource -ModuleName xWebAdministration

    Node "localhost" {

        # Cài IIS (Windows Feature)
        WindowsOptionalFeature IIS {
            Name   = "IIS-WebServer"
            Ensure = "Enable"
        }

        # Tạo thư mục web root
        File WebRoot {
            DestinationPath = "C:\inetpub\labsite"
            Ensure          = "Present"
            Type            = "Directory"
            DependsOn       = "[WindowsOptionalFeature]IIS"
        }

        # Tạo index.html
        File IndexPage {
            DestinationPath = "C:\inetpub\labsite\index.html"
            Ensure          = "Present"
            Contents        = "

Managed by PowerShell DSC

" DependsOn = "[File]WebRoot" } # Tạo website IIS xWebsite LabSite { Name = "LabSite" Ensure = "Present" State = "Started" PhysicalPath = "C:\inetpub\labsite" BindingInfo = @( MSFT_xWebBindingInformation { Protocol = "HTTP"; Port = 8080 } ) DependsOn = "[File]IndexPage" } # Tạo local user (service account) User AppServiceAccount { UserName = "svc-webapp" Ensure = "Present" FullName = "Web App Service Account" Password = (New-Object PSCredential("svc-webapp", (ConvertTo-SecureString "P@ssw0rd!Lab" -AsPlainText -Force))) PasswordNeverExpires = $true Disabled = $false } # Đảm bảo Windows Firewall bật Service WindowsFirewall { Name = "MpsSvc" StartupType = "Automatic" State = "Running" } } } '@ $configScript | Set-Content -Path "C:\DSC\WebServerConfig.ps1" -Encoding UTF8 # 3. Tạo thư mục và chạy Configuration (compile ra MOF) New-Item -ItemType Directory -Path "C:\DSC" -Force | Out-Null . "C:\DSC\WebServerConfig.ps1" WebServerBaseline -OutputPath "C:\DSC\MOF" # Output: C:\DSC\MOF\localhost.mof # 4. Apply configuration (Push mode) Start-DscConfiguration -Path "C:\DSC\MOF" -Wait -Verbose -Force # 5. Kiểm tra trạng thái Test-DscConfiguration -Detailed # InDesiredState: True nghĩa là mọi thứ đúng cấu hình # 6. Mô phỏng drift: xóa index.html Remove-Item "C:\inetpub\labsite\index.html" -Force # 7. Test lại - phát hiện drift $result = Test-DscConfiguration -Detailed $result.ResourcesNotInDesiredState # Sẽ thấy File[IndexPage] bị drift # 8. Tự fix: chạy lại Apply Start-DscConfiguration -Path "C:\DSC\MOF" -Wait -Verbose -Force # 9. Verify website Invoke-WebRequest -Uri "http://localhost:8080/" -UseBasicParsing | Select-Object -Expand Content

✅ Kết quả mong đợi: Test-DscConfiguration ban đầu trả về InDesiredState: True. Sau khi xóa file (drift), test trả về False với resource drift cụ thể. Sau khi apply lại, Invoke-WebRequest trả về "Managed by PowerShell DSC".

🧹 Cleanup: Remove-Website -Name "LabSite", Remove-LocalUser -Name "svc-webapp", Remove-Item C:\DSC -Recurse -Force.

LAB-094

cloud-init bootstrap VM trên cloud (Azure/AWS)

cloud-init · az CLI / aws CLI

🎯 Mục tiêu: Viết cloud-init user-data YAML tự động cài Docker, tạo user devops, deploy SSH key, chạy container ngay lần boot đầu — kiểm tra log cloud-init.

🧰 Công cụ / nền tảng: az CLI hoặc aws CLI, SSH key pair, Ubuntu 22.04 LTS image.

📦 Chuẩn bị: Đăng nhập az login (Azure) hoặc aws configure (AWS). Có SSH key tại ~/.ssh/id_rsa.pub.

▶️ Các bước (CLI):

# 1. Tạo cloud-init user-data file
cat > cloud-init.yaml << 'EOF'
#cloud-config
# Lab 094: Bootstrap VM với Docker + user + SSH key

package_update: true
package_upgrade: false

packages:
  - curl
  - git
  - ca-certificates
  - apt-transport-https

users:
  - name: devops
    groups: [sudo, docker]
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - ssh-rsa AAAA...YOUR_PUBLIC_KEY_HERE... lab-vm-key

write_files:
  - path: /etc/docker/daemon.json
    content: |
      {"log-driver": "json-file", "log-opts": {"max-size": "10m", "max-file": "3"}}
    permissions: '0644'
  - path: /opt/motd-lab.sh
    content: |
      #!/bin/bash
      echo "=== Lab VM managed by cloud-init ==="
      echo "Docker: $(docker --version 2>/dev/null || echo 'not ready')"
    permissions: '0755'

runcmd:
  # Cài Docker Engine
  - curl -fsSL https://get.docker.com | sh
  # Thêm user devops vào group docker (đảm bảo)
  - usermod -aG docker devops
  # Chạy container nginx test
  - docker run -d --name web --restart=unless-stopped -p 80:80 nginx:alpine
  # Ghi cloud-init completion marker
  - echo "cloud-init completed at $(date)" > /var/log/cloud-init-lab.log
  - bash /opt/motd-lab.sh >> /var/log/cloud-init-lab.log

final_message: "Lab VM ready! Elapsed: $UPTIME seconds"
EOF

# --- AZURE ---
# 2a. Tạo Resource Group và VM
az group create --name rg-cloudinit-lab --location southeastasia

az vm create \
  --resource-group rg-cloudinit-lab \
  --name vm-cloudinit-lab \
  --image Ubuntu2204 \
  --size Standard_B1s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --custom-data cloud-init.yaml \
  --output table

# Lấy public IP
PUBLIC_IP=$(az vm show -d -g rg-cloudinit-lab -n vm-cloudinit-lab --query publicIps -o tsv)
echo "VM IP: $PUBLIC_IP"

# --- AWS (alternative) ---
# 2b. Tạo EC2 instance với user-data
# aws ec2 run-instances \
#   --image-id ami-0c55b159cbfafe1f0 \
#   --count 1 \
#   --instance-type t3.micro \
#   --key-name my-key \
#   --user-data file://cloud-init.yaml \
#   --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=lab-cloudinit}]'

# 3. Chờ VM sẵn sàng (~2 phút), SSH vào kiểm tra
ssh -i ~/.ssh/id_rsa azureuser@$PUBLIC_IP

# 4. Trong VM: kiểm tra cloud-init log
sudo cat /var/log/cloud-init-output.log | tail -30
cat /var/log/cloud-init-lab.log

# 5. Kiểm tra Docker container chạy
docker ps
curl http://localhost/  # nginx welcome page

# 6. Kiểm tra user devops
id devops
sudo -l -U devops

✅ Kết quả mong đợi: /var/log/cloud-init-output.log kết thúc bằng "Lab VM ready!". docker ps thấy container web running. curl http://localhost/ trả về Nginx welcome page. User devops tồn tại với quyền sudo.

🧹 Cleanup: az group delete --name rg-cloudinit-lab --yes --no-wait (xóa toàn bộ resource group Azure). AWS: aws ec2 terminate-instances --instance-ids i-xxxx.

LAB-095

Ansible hardening baseline (CIS Benchmark)

ansible · ansible-galaxy · security

🎯 Mục tiêu: Dùng community CIS hardening role (dev-sec.os-hardening) áp dụng baseline security config, kiểm tra với ansible-audit / manual check, hiểu cơ chế các control hoạt động.

🧰 Công cụ / nền tảng: ansible, ansible-galaxy, Linux target Ubuntu 22.04, VS Code.

📦 Chuẩn bị: Môi trường từ LAB-091. VM test riêng (không dùng production).

▶️ Các bước:

# 1. Cài community hardening role từ Ansible Galaxy
ansible-galaxy role install dev-sec.os-hardening
# Hoặc dùng requirements.yml:
cat > requirements.yml << 'EOF'
---
roles:
  - name: dev-sec.os-hardening
    version: "9.2.0"
  - name: dev-sec.ssh-hardening
    version: "9.2.0"
EOF
ansible-galaxy role install -r requirements.yml

# 2. Tạo playbook hardening
cat > hardening.yml << 'EOF'
---
- name: Apply CIS-aligned hardening baseline
  hosts: webservers
  become: true

  vars:
    # os-hardening tuning
    os_auth_pam_passwdqc_enable: false       # dùng pam_pwquality thay thế
    os_auth_pw_max_age: 90
    os_auth_pw_min_age: 7
    os_auth_retries: 5
    os_auth_lockout_time: 600
    os_security_kernel_enable_sysrq: false
    os_security_suid_sgid_enforce: true
    os_security_users_allow: []              # không cho phép user login ngoài whitelist
    ufw_manage_defaults: true
    ufw_default_input_policy: "deny"
    ufw_default_output_policy: "allow"
    ufw_rules:
      - rule: allow
        to_port: "22"
        proto: tcp
        comment: "SSH"
      - rule: allow
        to_port: "80"
        proto: tcp
        comment: "HTTP"

    # ssh-hardening tuning
    ssh_permit_root_login: "no"
    ssh_password_authentication: "no"
    ssh_allow_tcp_forwarding: "no"
    ssh_client_alive_interval: 300
    ssh_client_alive_count_max: 2

  roles:
    - dev-sec.os-hardening
    - dev-sec.ssh-hardening
EOF

# 3. Dry-run (check mode) — không thay đổi thực tế
ansible-playbook -i inventory.ini hardening.yml --check --diff

# 4. Xem những gì sẽ thay đổi và xác nhận
# Chạy thực sự
ansible-playbook -i inventory.ini hardening.yml

# 5. Kiểm tra sau hardening
# Kiểm tra SSH config
ssh -o StrictHostKeyChecking=no user@localhost cat /etc/ssh/sshd_config | \
  grep -E "PermitRootLogin|PasswordAuthentication|AllowTcpForwarding"

# Kiểm tra sysctl hardening
sysctl net.ipv4.conf.all.accept_redirects
sysctl kernel.dmesg_restrict

# Kiểm tra SUID/SGID files (should be minimal)
find / -perm /6000 -type f 2>/dev/null | grep -v proc | head -20

# Kiểm tra password policy
grep -E "PASS_MAX_DAYS|PASS_MIN_DAYS" /etc/login.defs

# 6. Tự viết thêm custom hardening task
cat >> hardening.yml << 'EOF'
  post_tasks:
    - name: Disable unused filesystems (CIS 1.1.x)
      ansible.builtin.copy:
        dest: /etc/modprobe.d/disable-filesystems.conf
        content: |
          install cramfs /bin/true
          install freevxfs /bin/true
          install jffs2 /bin/true
          install hfs /bin/true
          install hfsplus /bin/true
          install udf /bin/true
        mode: '0644'

    - name: Set umask to 027 in /etc/profile.d/
      ansible.builtin.copy:
        dest: /etc/profile.d/hardening-umask.sh
        content: "umask 027\n"
        mode: '0644'

    - name: Ensure auditd is installed and running
      ansible.builtin.package:
        name: auditd
        state: present

    - name: Enable auditd service
      ansible.builtin.service:
        name: auditd
        state: started
        enabled: true
EOF

ansible-playbook -i inventory.ini hardening.yml --tags never,post_tasks 2>/dev/null || \
ansible-playbook -i inventory.ini hardening.yml

✅ Kết quả mong đợi: Playbook hoàn thành với 0 failed. SSH config có PermitRootLogin no, PasswordAuthentication no. Filesystem cramfs/jffs2 bị disable. auditd running. sysctl net.ipv4.conf.all.accept_redirects trả về 0.

🧹 Cleanup: Snapshot VM trước khi hardening hoặc dùng VM test riêng — hardening thay đổi system config sâu, khó rollback thủ công. Khuyên dùng immutable image sau hardening.

3. Tình huống doanh nghiệp thực tế

Bối cảnh

Một công ty fintech có 200 VM Linux (app server) và 50 VM Windows (report server). Sau một cuộc audit bảo mật, họ phát hiện 40% server có cấu hình SSH sai, password policy không đồng nhất và nhiều package lỗi thời. Ops team phải fix thủ công mất 3 tuần. Yêu cầu: không bao giờ để tình trạng này xảy ra lại.

Cách xử lý (Configuration Management)

  • Linux fleet: Dùng Ansible AWX (UI của Ansible) + dynamic inventory từ cloud API. Một hardening role chuẩn hóa từ CIS Benchmark chạy định kỳ qua scheduled job — đảm bảo drift tự được phát hiện và fix.
  • Windows fleet: PowerShell DSC với LCM mode ApplyAndAutoCorrect — node tự pull config từ Azure Automation State Configuration mỗi 30 phút và tự sửa drift không cần can thiệp.
  • New VM bootstrap: cloud-init inject qua Terraform custom_data cài Ansible agent và join vào AWX inventory ngay lần boot đầu — "zero-touch provisioning".
  • Compliance reporting: Ansible callback plugin ghi kết quả vào ELK; dashboard cho biết bao nhiêu % fleet đang in-desired-state.
  • Kết quả: Thời gian audit tiếp theo giảm từ 3 tuần xuống 2 giờ (report tự động). Drift bị phát hiện trong <30 phút thay vì months.

📚 Nguồn tham khảo

Module 18: IaC Module 20: GitOps với Argo CD & Flux
Zalo