Introduction
The ansible.windows.win_file module manages files and directories on Windows targets — create directories, delete files, create symbolic links, and set attributes. It's the Windows equivalent of ansible.builtin.file for Linux. Every Windows automation playbook uses it for directory setup, cleanup, and file organization.
Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
path | ✅ | — | Target file or directory path |
state | ❌ | file | file, directory, touch, absent, link |
force | ❌ | false | Force creation of symlinks |
State values:
| State | Description |
|---|---|
directory | Create directory (and parents) if it doesn't exist |
touch | Create empty file or update timestamp |
absent | Delete file or directory recursively |
file | Assert file exists (fail if not) |
link | Create symbolic link (requires src) |
Create Directories
---
- name: Create directory structures on Windows
hosts: windows
tasks:
- name: Create a single directory
ansible.windows.win_file:
path: C:\Apps\MyApplication
state: directory
- name: Create nested directories (parents created automatically)
ansible.windows.win_file:
path: C:\Apps\MyApplication\logs\archive\2026
state: directory
- name: Create multiple directories
ansible.windows.win_file:
path: "{{ item }}"
state: directory
loop:
- C:\Apps\MyApplication\config
- C:\Apps\MyApplication\data
- C:\Apps\MyApplication\logs
- C:\Apps\MyApplication\temp
- C:\Apps\MyApplication\backups
Create Empty Files
---
- name: Create empty files (touch)
hosts: windows
tasks:
- name: Create an empty log file
ansible.windows.win_file:
path: C:\Apps\MyApplication\logs\app.log
state: touch
- name: Touch multiple files
ansible.windows.win_file:
path: "{{ item }}"
state: touch
loop:
- C:\Apps\MyApplication\logs\access.log
- C:\Apps\MyApplication\logs\error.log
- C:\Apps\MyApplication\data\.initialized
Delete Files and Directories
---
- name: Clean up files and directories
hosts: windows
tasks:
- name: Delete a single file
ansible.windows.win_file:
path: C:\Temp\installer.exe
state: absent
- name: Delete a directory recursively
ansible.windows.win_file:
path: C:\Apps\OldApplication
state: absent
- name: Clean up temp files
ansible.windows.win_file:
path: "{{ item }}"
state: absent
loop:
- C:\Temp\cache
- C:\Temp\downloads
- C:\Users\svcaccount\AppData\Local\Temp
Symbolic Links
---
- name: Manage symbolic links
hosts: windows
tasks:
- name: Create symlink to current version
ansible.windows.win_file:
src: C:\Apps\MyApplication\v2.1.0
path: C:\Apps\MyApplication\current
state: link
- name: Update symlink to new version (force overwrite)
ansible.windows.win_file:
src: C:\Apps\MyApplication\v2.2.0
path: C:\Apps\MyApplication\current
state: link
force: true
Practical Patterns
Application Deployment Directory Setup
---
- name: Prepare application directory structure
hosts: windows
vars:
app_name: "WebPortal"
app_root: "D:\\Applications\\{{ app_name }}"
app_version: "3.1.0"
tasks:
- name: Create application directories
ansible.windows.win_file:
path: "{{ item }}"
state: directory
loop:
- "{{ app_root }}"
- "{{ app_root }}\\releases"
- "{{ app_root }}\\releases\\{{ app_version }}"
- "{{ app_root }}\\shared"
- "{{ app_root }}\\shared\\config"
- "{{ app_root }}\\shared\\logs"
- "{{ app_root }}\\shared\\data"
- name: Point current to new release
ansible.windows.win_file:
src: "{{ app_root }}\\releases\\{{ app_version }}"
path: "{{ app_root }}\\current"
state: link
force: true
IIS Site Setup
---
- name: Set up IIS web application directories
hosts: webservers
vars:
sites:
- { name: "main-site", port: 80 }
- { name: "api", port: 8080 }
- { name: "admin", port: 8443 }
tasks:
- name: Create web root directories
ansible.windows.win_file:
path: "C:\\inetpub\\{{ item.name }}"
state: directory
loop: "{{ sites }}"
- name: Create log directories
ansible.windows.win_file:
path: "C:\\inetpub\\logs\\{{ item.name }}"
state: directory
loop: "{{ sites }}"
Cleanup Old Releases
---
- name: Clean up old application releases
hosts: windows
vars:
app_root: "D:\\Applications\\WebPortal"
keep_releases: 3
tasks:
- name: List release directories
ansible.windows.win_find:
paths: "{{ app_root }}\\releases"
file_type: directory
register: releases
- name: Sort releases by date (oldest first)
ansible.builtin.set_fact:
old_releases: >-
{{ releases.files
| sort(attribute='creationtime')
| map(attribute='path')
| list }}
- name: Remove old releases (keep latest N)
ansible.windows.win_file:
path: "{{ item }}"
state: absent
loop: "{{ old_releases[:-keep_releases] }}"
when: (old_releases | length) > keep_releases
Check File Exists Before Acting
---
- name: Conditional operations based on file existence
hosts: windows
tasks:
- name: Check if config exists
ansible.windows.win_stat:
path: C:\Apps\MyApp\config.json
register: config_file
- name: Create default config if missing
ansible.windows.win_copy:
content: '{"version": "1.0", "debug": false}'
dest: C:\Apps\MyApp\config.json
when: not config_file.stat.exists
- name: Check if legacy directory exists
ansible.windows.win_stat:
path: C:\Apps\LegacyApp
register: legacy_dir
- name: Remove legacy application
ansible.windows.win_file:
path: C:\Apps\LegacyApp
state: absent
when: legacy_dir.stat.exists
win_file vs Other Windows File Modules
| Module | Use For |
|---|---|
win_file | Create/delete dirs, touch files, symlinks |
win_copy | Copy files (local→remote or remote→remote) |
win_template | Deploy Jinja2 templates |
win_stat | Check if file/dir exists, get attributes |
win_find | Search for files by pattern/age/size |
win_acl | Set NTFS permissions (ACLs) |
win_owner | Change file ownership |
win_lineinfile | Edit lines in text files |
Troubleshooting
"Access is denied"
The Ansible WinRM user doesn't have permissions on the target path. Either use a privileged account or set ACLs:
- name: Grant service account access
ansible.windows.win_acl:
path: "C:\\Apps\\MyApp"
user: "DOMAIN\\svc_ansible"
rights: FullControl
type: allow
state: present
inherit: ContainerInherit, ObjectInherit
"The system cannot find the path specified"
The parent directory doesn't exist. state: directory creates parents automatically, but state: touch and state: file do not:
# This fails if C:\Apps\NewApp doesn't exist
- ansible.windows.win_file:
path: C:\Apps\NewApp\app.log
state: touch
# Fix: create parent first
- ansible.windows.win_file:
path: C:\Apps\NewApp
state: directory
- ansible.windows.win_file:
path: C:\Apps\NewApp\app.log
state: touch
"Cannot remove directory — in use"
A process has a file open or the directory is a current working directory. Stop the service first:
- name: Stop service before cleanup
ansible.windows.win_service:
name: MyAppService
state: stopped
- name: Remove application directory
ansible.windows.win_file:
path: C:\Apps\MyApp
state: absent
- name: Reinstall application
ansible.windows.win_file:
path: C:\Apps\MyApp
state: directory
Related Articles
- Copy Files to Windows
- Windows Backup with win_copy & win_robocopy
- Configure Windows for Ansible (WinRM)
- Windows Enterprise Fleet Management
- Create a Directory (Linux file module)
- Check if File Exists (stat module)
Conclusion
ansible.windows.win_file handles four jobs on Windows: create directories (state: directory), create empty files (state: touch), delete files or directories (state: absent), and create symlinks (state: link). Parent directories are created automatically with state: directory. For file content, use win_copy or win_template. For permissions, use win_acl. For existence checks, use win_stat. The module is idempotent — running it twice produces the same result.