diff --git a/CHANGELOG.md b/CHANGELOG.md
index 874aed39af..70f4518137 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -233,6 +233,7 @@ Ansible Changes By Release
- web_infrastructure
* jenkins_script
- windows:
+ * win_find
* win_path
* win_psexec
* win_say
diff --git a/lib/ansible/modules/windows/win_find.ps1 b/lib/ansible/modules/windows/win_find.ps1
new file mode 100644
index 0000000000..2e7bdb40ef
--- /dev/null
+++ b/lib/ansible/modules/windows/win_find.ps1
@@ -0,0 +1,347 @@
+#!powershell
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see .
+
+# WANT_JSON
+# POWERSHELL_COMMON
+
+$ErrorActionPreference = "Stop"
+
+$params = Parse-Args -arguments $args -supports_check_mode $true
+
+$paths = Get-AnsibleParam -obj $params -name 'paths' -failifempty $true
+
+$age = Get-AnsibleParam -obj $params -name 'age' -failifempty $false -default $null
+$age_stamp = Get-AnsibleParam -obj $params -name 'age_stamp' -failifempty $false -default 'mtime' -ValidateSet 'mtime','ctime','atime'
+$file_type = Get-AnsibleParam -obj $params -name 'file_type' -failifempty $false -default 'file' -ValidateSet 'file','directory'
+$follow = Get-AnsibleParam -obj $params -name 'follow' -type "bool" -failifempty $false -default $false
+$hidden = Get-AnsibleParam -obj $params -name 'hidden' -type "bool" -failifempty $false -default $false
+$patterns = Get-AnsibleParam -obj $params -name 'patterns' -failifempty $false -default $null
+$recurse = Get-AnsibleParam -obj $params -name 'recurse' -type "bool" -failifempty $false -default $false
+$size = Get-AnsibleParam -obj $params -name 'size' -failifempty $false -default $null
+$use_regex = Get-AnsibleParam -obj $params -name 'use_regex' -type "bool" -failifempty $false -default $false
+$get_checksum = Get-AnsibleParam -obj $params -name 'get_checksum' -type "bool" -failifempty $false -default $true
+$checksum_algorithm = Get-AnsibleParam -obj $params -name 'checksum_algorithm' -failifempty $false -default 'sha1' -ValidateSet 'md5', 'sha1', 'sha256', 'sha384', 'sha512'
+
+$result = @{
+ files = @()
+ warnings = @()
+ examined = 0
+ matched = 0
+ changed = $false
+}
+
+# C# code to determine link target, copied from http://chrisbensen.blogspot.com.au/2010/06/getfinalpathnamebyhandle.html
+$symlink_util = @"
+using System;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+
+namespace Ansible.Command {
+ public class SymLinkHelper {
+ private const int FILE_SHARE_WRITE = 2;
+ private const int CREATION_DISPOSITION_OPEN_EXISTING = 3;
+ private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
+
+ [DllImport("kernel32.dll", EntryPoint = "GetFinalPathNameByHandleW", CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern int GetFinalPathNameByHandle(IntPtr handle, [In, Out] StringBuilder path, int bufLen, int flags);
+
+ [DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess,
+ int dwShareMode, IntPtr SecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
+
+ public static string GetSymbolicLinkTarget(System.IO.DirectoryInfo symlink) {
+ SafeFileHandle directoryHandle = CreateFile(symlink.FullName, 0, 2, System.IntPtr.Zero, CREATION_DISPOSITION_OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, System.IntPtr.Zero);
+ if(directoryHandle.IsInvalid)
+ throw new Win32Exception(Marshal.GetLastWin32Error());
+
+ StringBuilder path = new StringBuilder(512);
+ int size = GetFinalPathNameByHandle(directoryHandle.DangerousGetHandle(), path, path.Capacity, 0);
+
+ if (size<0)
+ throw new Win32Exception(Marshal.GetLastWin32Error()); // The remarks section of GetFinalPathNameByHandle mentions the return being prefixed with "\\?\" // More information about "\\?\" here -> http://msdn.microsoft.com/en-us/library/aa365247(v=VS.85).aspx
+ if (path[0] == '\\' && path[1] == '\\' && path[2] == '?' && path[3] == '\\')
+ return path.ToString().Substring(4);
+ else
+ return path.ToString();
+ }
+ }
+}
+"@
+Add-Type -TypeDefinition $symlink_util
+
+Function Assert-Age($info) {
+ $valid_match = $true
+
+ if ($age -ne $null) {
+ $seconds_per_unit = @{'s'=1; 'm'=60; 'h'=3600; 'd'=86400; 'w'=604800}
+ $seconds_pattern = '^(-?\d+)(s|m|h|d|w)?$'
+ $match = $age -match $seconds_pattern
+ if ($match) {
+ [int]$specified_seconds = $matches[1]
+ if ($matches[2] -eq $null) {
+ $chosen_unit = 's'
+ } else {
+ $chosen_unit = $matches[2]
+ }
+
+ $abs_seconds = $specified_seconds * ($seconds_per_unit.$chosen_unit)
+ $epoch = New-Object -Type DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0
+ if ($age_stamp -eq 'mtime') {
+ $age_comparison = $epoch.AddSeconds($info.lastwritetime)
+ } elseif ($age_stamp -eq 'ctime') {
+ $age_comparison = $epoch.AddSeconds($info.creationtime)
+ } elseif ($age_stamp -eq 'atime') {
+ $age_comparison = $epoch.AddSeconds($info.lastaccesstime)
+ }
+
+ if ($specified_seconds -ge 0) {
+ $start_date = (Get-Date).AddSeconds($abs_seconds * -1)
+ if ($age_comparison -lt $start_date) {
+ $valid_match = $false
+ }
+ } else {
+ $start_date = (Get-Date).AddSeconds($abs_seconds)
+ if ($age_comparison -gt $start_date) {
+ $valid_match = $false
+ }
+ }
+ } else {
+ Fail-Json $result "failed to process age"
+ }
+ }
+
+ $valid_match
+}
+
+Function Assert-FileType($info) {
+ $valid_match = $true
+
+ if ($file_type -eq 'directory' -and $info.isdir -eq $false) {
+ $valid_match = $false
+ }
+ if ($file_type -eq 'file' -and $info.isdir -eq $true) {
+ $valid_match = $false
+ }
+
+ $valid_match
+}
+
+Function Assert-Hidden($info) {
+ $valid_match = $true
+
+ if ($hidden -eq $true -and $info.ishidden -eq $false) {
+ $valid_match = $false
+ }
+ if ($hidden -eq $false -and $info.ishidden -eq $true) {
+ $valid_match = $false
+ }
+
+ $valid_match
+}
+
+Function Assert-Pattern($info) {
+ $valid_match = $false
+
+ if ($patterns -ne $null) {
+ foreach ($pattern in $patterns) {
+ if ($use_regex -eq $true) {
+ # Use -match for regex matching
+ if ($info.filename -match $pattern) {
+ $valid_match = $true
+ }
+ } else {
+ # Use -like for wildcard matching
+ if ($info.filename -like $pattern) {
+ $valid_match = $true
+ }
+ }
+ }
+ } else {
+ $valid_match = $true
+ }
+
+ $valid_match
+}
+
+Function Assert-Size($info) {
+ $valid_match = $true
+
+ if ($size -ne $null) {
+ $bytes_per_unit = @{'b'=1; 'k'=1024; 'm'=1024*1024; 'g'=1024*1024*1024; 't'=1024*1024*1024*1024}
+ $size_pattern = '^(-?\d+)(b|k|m|g|t)?$'
+ $match = $size -match $size_pattern
+ if ($match) {
+ [int]$specified_size = $matches[1]
+ if ($matches[2] -eq $null) {
+ $chosen_byte = 'b'
+ } else {
+ $chosen_byte = $matches[2]
+ }
+
+ $abs_size = $specified_size * ($bytes_per_unit.$chosen_byte)
+ if ($specified_size -ge 0) {
+ if ($info.size -lt $abs_size) {
+ $valid_match = $false
+ }
+ } else {
+ if ($info.size -gt $abs_size * -1) {
+ $valid_match = $false
+ }
+ }
+ } else {
+ Fail-Json $result "failed to process size"
+ }
+ }
+
+ $valid_match
+}
+
+Function Assert-FileStat($info) {
+ $age_match = Assert-Age -info $info
+ $file_type_match = Assert-FileType -info $info
+ $hidden_match = Assert-Hidden -info $info
+ $pattern_match = Assert-Pattern -info $info
+ $size_match = Assert-Size -info $info
+
+ if ($age_match -and $file_type_match -and $hidden_match -and $pattern_match -and $size_match) {
+ $info
+ } else {
+ $false
+ }
+}
+
+Function Get-FileStat($file) {
+ $epoch = New-Object -Type DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0
+ $access_control = $file.GetAccessControl()
+ $attributes = @()
+ foreach ($attribute in ($file.Attributes -split ',')) {
+ $attributes += $attribute.Trim()
+ }
+
+ $file_stat = @{
+ isreadonly = $attributes -contains 'ReadOnly'
+ ishidden = $attributes -contains 'Hidden'
+ isarchive = $attributes -contains 'Archive'
+ attributes = $file.Attributes.ToString()
+ owner = $access_control.Owner
+ lastwritetime = (New-TimeSpan -Start $epoch -End $file.LastWriteTime).TotalSeconds
+ creationtime = (New-TimeSpan -Start $epoch -End $file.CreationTime).TotalSeconds
+ lastaccesstime = (New-TimeSpan -Start $epoch -End $file.LastAccessTime).TotalSeconds
+ path = $file.FullName
+ filename = $file.Name
+ }
+
+ $islink = $false
+ $isdir = $false
+ $isshared = $false
+
+ if ($attributes -contains 'ReparsePoint') {
+ # TODO: Find a way to differenciate between soft and junction links
+ $islink = $true
+ $isdir = $true
+
+ # Try and get the symlink source, can result in failure if link is broken
+ try {
+ $lnk_source = [Ansible.Command.SymLinkHelper]::GetSymbolicLinkTarget($file)
+ $file_stat.lnk_source = $lnk_source
+ } catch {}
+ } elseif ($file.PSIsContainer) {
+ $isdir = $true
+
+ $share_info = Get-WmiObject -Class Win32_Share -Filter "Path='$($file.Fullname -replace '\\', '\\')'"
+ if ($share_info -ne $null) {
+ $isshared = $true
+ $file_stat.sharename = $share_info.Name
+ }
+
+ #$dir_files_sum = Get-ChildItem $file.FullName -Recurse | Measure-Object -property length -sum
+ $dir_files_sum = Get-ChildItem $file.FullName -Recurse
+
+ if ($dir_files_sum -eq $null -or ($dir_files_sum.PSObject.Properties.name -contains 'length' -eq $false)) {
+ $file_stat.size = 0
+ } else {
+ $file_stat.size = ($dir_files_sum | Measure-Object -property length -sum).Sum
+ }
+ } else {
+ $file_stat.size = $file.length
+ $file_stat.extension = $file.Extension
+
+ if ($get_checksum) {
+ $checksum = Get-FileChecksum -path $path -algorithm $checksum_algorithm
+ $file_stat.checksum = $checksum
+ }
+ }
+
+ $file_stat.islink = $islink
+ $file_stat.isdir = $isdir
+ $file_stat.isshared = $isshared
+
+ Assert-FileStat -info $file_stat
+}
+
+Function Get-FilesInFolder($path) {
+ $items = @()
+ foreach ($item in (Get-ChildItem -Force -Path $path -ErrorAction SilentlyContinue)) {
+ if ($item.PSIsContainer -and $recurse) {
+ if (($item.Attributes -like '*ReparsePoint*' -and $follow) -or ($item.Attributes -notlike '*ReparsePoint*')) {
+ # File is a link and we want to follow a link OR file is not a link
+ $items += $item.FullName
+ $items += Get-FilesInFolder -path $item.FullName
+ } else {
+ # File is a link but we don't want to follow a link
+ $items += $item.FullName
+ }
+ } else {
+ $items += $item.FullName
+ }
+ }
+
+ $items
+}
+
+$paths_to_check = @()
+foreach ($path in $paths) {
+ if (Test-Path $path) {
+ if ((Get-Item -Force $path).PSIsContainer) {
+ $paths_to_check += Get-FilesInFolder -path $path
+ } else {
+ Fail-Json $result "Argument path $path is a file not a directory"
+ }
+ } else {
+ Fail-Json $result "Argument path $path does not exist cannot get information on"
+ }
+}
+$paths_to_check = $paths_to_check | Select -Unique
+
+foreach ($path in $paths_to_check) {
+ $file = Get-Item -Force -Path $path
+ $info = Get-FileStat -file $file
+ $new_examined = $result.examined + 1
+ $result.examined = $new_examined
+
+ if ($info -ne $false) {
+ $files = $result.Files
+ $files += $info
+
+ $new_matched = $result.matched + 1
+ $result.matched = $new_matched
+ $result.files = $files
+ }
+}
+
+Exit-Json $result
diff --git a/lib/ansible/modules/windows/win_find.py b/lib/ansible/modules/windows/win_find.py
new file mode 100644
index 0000000000..194621c730
--- /dev/null
+++ b/lib/ansible/modules/windows/win_find.py
@@ -0,0 +1,323 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# (c) 2016, Ansible, inc
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see .
+#
+
+ANSIBLE_METADATA = {'status': ['preview'],
+ 'supported_by': 'community',
+ 'version': '1.0'}
+
+DOCUMENTATION = r'''
+---
+module: win_find
+version_added: "2.3"
+short_description: return a list of files based on specific criteria
+description:
+ - Return a list of files based on specified criteria.
+ - Multiple criteria are AND'd together.
+options:
+ age:
+ description:
+ - Select files or folders whose age is equal to or greater than
+ the specified tim. Use a negative age to find files equal to or
+ less than the specified time. You can choose seconds, minues,
+ hours, days or weeks by specifying the first letter of an of
+ those words (e.g., "1w").
+ required: false
+ age_stamp:
+ description:
+ - Choose the file property against which we compare C(age). The
+ default attribute we compare with is last modification time.
+ required: false
+ default: mtime
+ choices: ['atime', 'mtime', 'ctime']
+ checksum_algorithm:
+ description:
+ - Algorithm to determine the checksum of a file. Will throw an error
+ if the host is unable to use specified algorithm.
+ required: false
+ default: sha1
+ choices: ['md5', 'sha1', 'sha256', 'sha384', 'sha512']
+ file_type:
+ description: Type of file to search for
+ required: false
+ default: file
+ choices: ['file', 'directory']
+ follow:
+ description:
+ - Set this to true to follow symlinks in the path. This needs to
+ be used in conjunction with C(recurse).
+ required: false
+ default: false
+ choices: ['true', 'false']
+ get_checksum:
+ description:
+ - Whether to return a checksum of the file in the return info (default sha1),
+ use C(checksum_algorithm) to change from the default.
+ required: false
+ default: true
+ choices: ['true', 'false']
+ hidden:
+ description: Set this to include hidden files or folders
+ required: false
+ default: false
+ choices: ['true', 'false']
+ paths:
+ description:
+ - List of paths of directories to search for files or folders in.
+ This can be supplied as a single path or a list of paths.
+ required: true
+ patterns:
+ description:
+ - One or more (powershell or regex) patterns to compare filenames
+ with. The type of pattern matching is controlled by C(use_regex)
+ option. The patterns retrict the list of files or folders to be
+ returned based on the filenames. For a file to be matched it
+ only has to match with one pattern in a list provided.
+ required: false
+ recurse:
+ description:
+ - Will recursively descend into the directory looking for files
+ or folders
+ required: false
+ default: false
+ choices: ['true', 'false']
+ size:
+ description:
+ - Select files or folders whose size is equal to or greater than
+ the specified size. Use a negative value to find files equal to
+ or less than the specified size. You can specify the size with
+ a suffix of the byte type i.e. kilo = k, mega = m... Size is not
+ evaluated for symbolic links.
+ required: false
+ default: false
+ use_regex:
+ description:
+ - Will set patterns to run as a regex check if true
+ required: false
+ default: false
+ choices: ['true', 'false']
+author: "Jordan Borean (@jborean93)"
+'''
+
+EXAMPLES = r'''
+# Find files in path
+- win_find:
+ paths: D:\temp
+
+# Find hidden files in path
+- win_find:
+ paths: D:\temp
+ hidden: True
+
+# Find files in multiple paths
+- win_find:
+ paths: ['C:\temp', 'D:\temp']
+
+# Find files in directory while searching recursively
+- win_find:
+ paths: D:\temp
+ recurse: True
+
+# Find files in directory while following symlinks
+- win_find:
+ paths: D:\temp
+ recurse: True
+ follow: True
+
+# Find files with .log and .out extension using powershell wildcards
+- win_find:
+ paths: D:\temp
+ patterns: ['*.log', '*.out']
+
+# Find files in path based on regex pattern
+- win_find:
+ paths: D:\temp
+ patterns: "out_\d{8}-\d{6}.log"
+
+# Find files older than 1 day
+- win_find:
+ paths: D:\temp
+ age: 86400
+
+# Find files older than 1 day based on create time
+- win_find:
+ paths: D:\temp
+ age: 86400
+ age_stamp: ctime
+
+# Find files older than 1 day with unit syntax
+- win_find:
+ paths: D:\temp
+ age: 1d
+
+# Find files newer than 1 hour
+- win_find:
+ paths: D:\temp
+ age: -3600
+
+# Find files newer than 1 hour with unit syntax
+- win_find:
+ paths: D:\temp
+ age: -1h
+
+# Find files larger than 1MB
+- win_find:
+ paths: D:\temp
+ size: 1048576
+
+# Find files larger than 1GB with unit syntax
+- win_find:
+ paths: D:\temp
+ size: 1g
+
+# Find files smaller than 1MB
+- win_find:
+ paths: D:\temp
+ size: -1048576
+
+# Find files smaller than 1GB with unit syntax
+- win_find:
+ paths: D:\temp
+ size: -1g
+
+# Find folders/symlinks in multiple paths
+- win_find:
+ paths: ['C:\temp', 'D:\temp']
+ file_type: directory
+
+# Find files and return SHA256 checksum of files found
+- win_find:
+ paths: C:\temp
+ get_checksum: True
+ checksum_algorithm: sha256
+
+# Find files and do not return the checksum
+- win_find:
+ path: C:\temp
+ get_checksum: False
+'''
+
+RETURN = r'''
+changed:
+ description: Whether anything was chagned
+ returned: always
+ type: boolean
+ sample: True
+examined:
+ description: The number of files/folders that was checked
+ returned: always
+ type: int
+ sample: 10
+matched:
+ description: The number of files/folders that match the criteria
+ returns: always
+ type: int
+ sample: 2
+files:
+ description: Information on the files/folders that match the criteria returned as a list of dictionary elements for each file matched
+ returned: success
+ type: dictionary
+ contains:
+ attributes:
+ description: attributes of the file at path in raw form
+ returned: success, path exists
+ type: string
+ sample: "Archive, Hidden"
+ checksum:
+ description: The checksum of a file based on checksum_algorithm specified
+ returned: success, path exists, path is a file, get_checksum == True
+ type: string
+ sample: 09cb79e8fc7453c84a07f644e441fd81623b7f98
+ creationtime:
+ description: the create time of the file represented in seconds since epoch
+ returned: success, path exists
+ type: float
+ sample: 1477984205.15
+ extension:
+ description: the extension of the file at path
+ returned: success, path exists, path is a file
+ type: string
+ sample: ".ps1"
+ isarchive:
+ description: if the path is ready for archiving or not
+ returned: success, path exists
+ type: boolean
+ sample: True
+ isdir:
+ description: if the path is a directory or not
+ returned: success, path exists
+ type: boolean
+ sample: True
+ ishidden:
+ description: if the path is hidden or not
+ returned: success, path exists
+ type: boolean
+ sample: True
+ islink:
+ description: if the path is a symbolic link or junction or not
+ returned: success, path exists
+ type: boolean
+ sample: True
+ isreadonly:
+ description: if the path is read only or not
+ returned: success, path exists
+ type: boolean
+ sample: True
+ isshared:
+ description: if the path is shared or not
+ returned: success, path exists
+ type: boolean
+ sample: True
+ lastaccesstime:
+ description: the last access time of the file represented in seconds since epoch
+ returned: success, path exists
+ type: float
+ sample: 1477984205.15
+ lastwritetime:
+ description: the last modification time of the file represented in seconds since epoch
+ returned: success, path exists
+ type: float
+ sample: 1477984205.15
+ lnk_source:
+ description: the target of the symbolic link, will return null if not a link or the link is broken
+ return: success, path exists, path is a symbolic link
+ type: string
+ sample: C:\temp
+ owner:
+ description: the owner of the file
+ returned: success, path exists
+ type: string
+ sample: BUILTIN\Administrators
+ path:
+ description: the full absolute path to the file
+ returned: success, path exists
+ type: string
+ sample: BUILTIN\Administrators
+ sharename:
+ description: the name of share if folder is shared
+ returned: success, path exists, path is a directory and isshared == True
+ type: string
+ sample: file-share
+ size:
+ description: the size in bytes of a file or folder
+ returned: success, path exists, path is not a link
+ type: int
+ sample: 1024
+'''
diff --git a/test/integration/targets/win_find/aliases b/test/integration/targets/win_find/aliases
new file mode 100644
index 0000000000..4a25390297
--- /dev/null
+++ b/test/integration/targets/win_find/aliases
@@ -0,0 +1 @@
+windows/ci/group/2
diff --git a/test/integration/targets/win_find/defaults/main.yml b/test/integration/targets/win_find/defaults/main.yml
new file mode 100644
index 0000000000..d3d03f767f
--- /dev/null
+++ b/test/integration/targets/win_find/defaults/main.yml
@@ -0,0 +1 @@
+win_find_dir: "{{win_output_dir}}\\win_find"
diff --git a/test/integration/targets/win_find/files/set_attributes.ps1 b/test/integration/targets/win_find/files/set_attributes.ps1
new file mode 100644
index 0000000000..b57a368c43
--- /dev/null
+++ b/test/integration/targets/win_find/files/set_attributes.ps1
@@ -0,0 +1,9 @@
+$path = $args[0]
+$attr = $args[1]
+$item = Get-Item "$path"
+
+$attributes = $item.Attributes -split ','
+If ($attributes -notcontains $attr) {
+ $attributes += $attr
+}
+$item.Attributes = $attributes -join ','
diff --git a/test/integration/targets/win_find/files/set_filedate.ps1 b/test/integration/targets/win_find/files/set_filedate.ps1
new file mode 100644
index 0000000000..3bc525a4ff
--- /dev/null
+++ b/test/integration/targets/win_find/files/set_filedate.ps1
@@ -0,0 +1,6 @@
+$date = Get-Date -Year 2016 -Month 11 -Day 1 -Hour 7 -Minute 10 -Second 5 -Millisecond 0
+
+$item = Get-Item -Path "$($args[0])"
+$item.CreationTime = $date
+$item.LastAccessTime = $date
+$item.LastWriteTime = $date
diff --git a/test/integration/targets/win_find/files/set_share.ps1 b/test/integration/targets/win_find/files/set_share.ps1
new file mode 100644
index 0000000000..c56242cf40
--- /dev/null
+++ b/test/integration/targets/win_find/files/set_share.ps1
@@ -0,0 +1,7 @@
+$share_name = $args[1]
+$share_stat = Get-WmiObject -Class Win32_Share -Filter "name='$share_name'"
+If ($share_stat) {
+ $share_stat.Delete()
+}
+$wmi = [wmiClass] 'Win32_Share'
+$wmi.Create($args[0], $share_name, 0)
diff --git a/test/integration/targets/win_find/meta/main.yml b/test/integration/targets/win_find/meta/main.yml
new file mode 100644
index 0000000000..d328716dfa
--- /dev/null
+++ b/test/integration/targets/win_find/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - prepare_win_tests
diff --git a/test/integration/targets/win_find/tasks/main.yml b/test/integration/targets/win_find/tasks/main.yml
new file mode 100644
index 0000000000..af6b2b7828
--- /dev/null
+++ b/test/integration/targets/win_find/tasks/main.yml
@@ -0,0 +1,754 @@
+---
+- name: remove links if they exist as win_file struggles
+ win_command: cmd.exe /c rmdir "{{item}}"
+ ignore_errors: True
+ with_items:
+ - "{{win_find_dir}}\\nested\\link"
+ - "{{win_find_dir}}\\broken-link"
+ - "{{win_find_dir}}\\hard-link-dest\\hard-link.log"
+ - "{{win_find_dir}}\\junction-link"
+
+- name: ensure the testing directory is cleared before setting up test
+ win_file:
+ path: "{{win_find_dir}}"
+ state: absent
+
+- name: ensure testing directories exist
+ win_file:
+ path: "{{item}}"
+ state: directory
+ with_items:
+ - "{{win_find_dir}}\\nested"
+ - "{{win_find_dir}}\\single"
+ - "{{win_find_dir}}\\link-dest"
+ - "{{win_find_dir}}\\link-dest\\sub-link"
+ - "{{win_find_dir}}\\hard-link-dest"
+ - "{{win_find_dir}}\\junction-link-dest"
+ - "{{win_find_dir}}\\broken-link-dest"
+ - "{{win_find_dir}}\\nested\\sub-nest"
+ - "{{win_find_dir}}\\shared"
+ - "{{win_find_dir}}\\shared\\folder"
+ - "{{win_find_dir}}\\hidden"
+ - "{{win_find_dir}}\\date"
+
+- name: create empty test files
+ win_file:
+ path: "{{item}}"
+ state: touch
+ with_items:
+ - "{{win_find_dir}}\\nested\\file.ps1"
+ - "{{win_find_dir}}\\nested\\test.ps1"
+ - "{{win_find_dir}}\\nested\\out.log"
+ - "{{win_find_dir}}\\nested\\archive.log"
+ - "{{win_find_dir}}\\nested\\sub-nest\\test.ps1"
+ - "{{win_find_dir}}\\nested\\sub-nest\\readonly.txt"
+ - "{{win_find_dir}}\\link-dest\\link.ps1"
+ - "{{win_find_dir}}\\single\\large.ps1"
+ - "{{win_find_dir}}\\single\\small.ps1"
+ - "{{win_find_dir}}\\single\\test.ps1"
+ - "{{win_find_dir}}\\single\\hidden.ps1"
+ - "{{win_find_dir}}\\date\\new.ps1"
+ - "{{win_find_dir}}\\date\\old.ps1"
+ - "{{win_find_dir}}\\single\\out_20161101-091005.log"
+ - "{{win_find_dir}}\\hidden\\out_20161101-091005.log"
+ - "{{win_find_dir}}\\hard-link-dest\\file-abc.log"
+
+- name: populate files with a test string
+ win_lineinfile:
+ dest: "{{item.path}}"
+ line: "{{item.text}}"
+ with_items:
+ - { 'path': "{{win_find_dir}}\\nested\\file.ps1", 'text': 'abcdefg1234567' }
+ - { 'path': "{{win_find_dir}}\\nested\\test.ps1", 'text': 'abcdefg1234567' }
+ - { 'path': "{{win_find_dir}}\\nested\\out.log", 'text': 'abcdefg1234567' }
+ - { 'path': "{{win_find_dir}}\\nested\\archive.log", 'text': 'abcdefg1234567' }
+ - { 'path': "{{win_find_dir}}\\nested\\sub-nest\\test.ps1", 'text': 'abcdefg1234567' }
+ - { 'path': "{{win_find_dir}}\\nested\\sub-nest\\readonly.txt", 'text': 'abcdefg1234567' }
+ - { 'path': "{{win_find_dir}}\\link-dest\\link.ps1", 'text': 'abcdefg1234567' }
+ - { 'path': "{{win_find_dir}}\\single\\test.ps1", 'text': 'abcdefg1234567' }
+ - { 'path': "{{win_find_dir}}\\single\\hidden.ps1", 'text': 'abcdefg1234567' }
+ - { 'path': "{{win_find_dir}}\\date\\new.ps1", 'text': 'abcdefg1234567' }
+ - { 'path': "{{win_find_dir}}\\date\\old.ps1", 'text': 'abcdefg1234567' }
+ - { 'path': "{{win_find_dir}}\\single\\out_20161101-091005.log", 'text': 'abcdefg1234567' }
+ - { 'path': "{{win_find_dir}}\\hidden\\out_20161101-091005.log", 'text': 'abcdefg1234567' }
+ - { 'path': "{{win_find_dir}}\\hard-link-dest\\file-abc.log", 'text': 'abcdefg1234567' }
+ - { 'path': "{{win_find_dir}}\\single\\small.ps1", 'text': "a" }
+ - { 'path': "{{win_find_dir}}\\date\\new.ps1", 'text': "random text for new date" }
+ - { 'path': "{{win_find_dir}}\\date\\old.ps1", 'text': "random text for old date" }
+
+- name: populate large text file
+ win_command: powershell "Set-Content {{win_find_dir}}\\single\\large.ps1 ('abcdefghijklmnopqrstuvwxyz' * 10000)"
+
+- name: create share
+ script: set_share.ps1 "{{win_find_dir}}\shared\folder" "folder-share"
+
+- name: create links
+ win_command: cmd.exe /c mklink /{{item.type}} "{{item.source}}" "{{item.target}}"
+ with_items:
+ - { type: 'D', source: "{{win_find_dir}}\\nested\\link", target: "{{win_find_dir}}\\link-dest" }
+ - { type: 'D', source: "{{win_find_dir}}\\broken-link", target: "{{win_find_dir}}\\broken-link-dest" }
+ - { type: 'H', source: "{{win_find_dir}}\\hard-link-dest\\hard-link.log", target: "{{win_find_dir}}\\hard-link-dest\\file-abc.log" }
+ - { type: 'J', source: "{{win_find_dir}}\\junction-link", target: "{{win_find_dir}}\\junction-link-dest" }
+
+- name: set modification date on files/folders
+ script: set_filedate.ps1 "{{item}}"
+ with_items:
+ - "{{win_find_dir}}\\nested\\file.ps1"
+ - "{{win_find_dir}}\\nested\\test.ps1"
+ - "{{win_find_dir}}\\nested\\out.log"
+ - "{{win_find_dir}}\\nested\\archive.log"
+ - "{{win_find_dir}}\\nested\\sub-nest\\test.ps1"
+ - "{{win_find_dir}}\\nested\\sub-nest\\readonly.txt"
+ - "{{win_find_dir}}\\link-dest\\link.ps1"
+ - "{{win_find_dir}}\\single\\large.ps1"
+ - "{{win_find_dir}}\\single\\small.ps1"
+ - "{{win_find_dir}}\\single\\test.ps1"
+ - "{{win_find_dir}}\\single\\hidden.ps1"
+ - "{{win_find_dir}}\\date\\old.ps1"
+ - "{{win_find_dir}}\\single\\out_20161101-091005.log"
+ - "{{win_find_dir}}\\hidden\\out_20161101-091005.log"
+ - "{{win_find_dir}}\\hard-link-dest\\file-abc.log"
+ - "{{win_find_dir}}\\nested"
+ - "{{win_find_dir}}\\single"
+ - "{{win_find_dir}}\\link-dest"
+ - "{{win_find_dir}}\\link-dest\\sub-link"
+ - "{{win_find_dir}}\\hard-link-dest"
+ - "{{win_find_dir}}\\junction-link-dest"
+ - "{{win_find_dir}}\\broken-link-dest"
+ - "{{win_find_dir}}\\nested\\sub-nest"
+ - "{{win_find_dir}}\\shared"
+ - "{{win_find_dir}}\\shared\\folder"
+ - "{{win_find_dir}}\\hidden"
+ - "{{win_find_dir}}\\date"
+
+- name: set file attributes for test
+ script: set_attributes.ps1 "{{item.path}}" {{item.attr}}
+ with_items:
+ - { 'path': "{{win_find_dir}}\\hidden", 'attr': "Hidden" }
+ - { 'path': "{{win_find_dir}}\\date", 'attr': "Hidden" }
+ - { 'path': "{{win_find_dir}}\\nested\\archive.log", 'attr': "Archive" }
+ - { 'path': "{{win_find_dir}}\\nested\\sub-nest\\readonly.txt", 'attr': "ReadOnly" }
+ - { 'path': "{{win_find_dir}}\\single\\hidden.ps1", 'attr': "Hidden" }
+
+- name: break the broken link target
+ win_file:
+ path: "{{win_find_dir}}\\broken-link-dest"
+ state: absent
+# end test setup
+
+- name: expect failure when not setting paths
+ win_find:
+ patterns: a
+ register: actual
+ failed_when: "actual.msg != 'Missing required argument: paths'"
+
+- name: expect failure when setting paths to a file
+ win_find:
+ paths: "{{win_output_dir}}\\win_find\\single\\large.ps1"
+ register: actual
+ failed_when: "actual.msg != 'Argument path {{win_output_dir|regex_replace('\\\\', '\\\\\\\\')}}\\win_find\\single\\large.ps1 is a file not a directory'"
+
+- name: expect failure whe path is set to a non existant folder
+ win_find:
+ paths: "{{win_output_dir}}\\win_find\\thisisafakefolder"
+ register: actual
+ failed_when: "actual.msg != 'Argument path {{win_output_dir|regex_replace('\\\\', '\\\\\\\\')}}\\\\win_find\\\\thisisafakefolder does not exist cannot get information on'"
+
+- name: get files in single directory
+ win_find:
+ paths: "{{win_output_dir}}\\win_find\\single"
+ register: actual
+
+- name: set expected value for files in a single directory
+ set_fact:
+ expected:
+ changed: False
+ examined: 5
+ files:
+ - { isarchive: True,
+ attributes: Archive,
+ checksum: f8d100cdcf0e6c1007db2f8dd0b7ee2884df89af,
+ creationtime: 1477984205,
+ extension: .ps1,
+ filename: large.ps1,
+ ishidden: False,
+ isdir: False,
+ islink: False,
+ lastaccesstime: 1477984205,
+ lastwritetime: 1477984205,
+ owner: BUILTIN\Administrators,
+ path: "{{win_find_dir}}\\single\\large.ps1",
+ isreadonly: False,
+ isshared: False,
+ size: 260002 }
+ - { isarchive: True,
+ attributes: Archive,
+ checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
+ creationtime: 1477984205,
+ extension: .log,
+ filename: out_20161101-091005.log,
+ ishidden: False,
+ isdir: False,
+ islink: False,
+ lastaccesstime: 1477984205,
+ lastwritetime: 1477984205,
+ owner: BUILTIN\Administrators,
+ path: "{{win_find_dir}}\\single\\out_20161101-091005.log",
+ isreadonly: False,
+ isshared: False,
+ size: 14 }
+ - { isarchive: True,
+ attributes: Archive,
+ checksum: 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8,
+ creationtime: 1477984205,
+ extension: .ps1,
+ filename: small.ps1,
+ ishidden: False,
+ isdir: False,
+ islink: False,
+ lastaccesstime: 1477984205,
+ lastwritetime: 1477984205,
+ owner: BUILTIN\Administrators,
+ path: "{{win_find_dir}}\\single\\small.ps1",
+ isreadonly: False,
+ isshared: False,
+ size: 1 }
+ - { isarchive: True,
+ attributes: Archive,
+ checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
+ creationtime: 1477984205,
+ extension: .ps1,
+ filename: test.ps1,
+ ishidden: False,
+ isdir: False,
+ islink: False,
+ lastaccesstime: 1477984205,
+ lastwritetime: 1477984205,
+ owner: BUILTIN\Administrators,
+ path: "{{win_find_dir}}\\single\\test.ps1",
+ isreadonly: False,
+ isshared: False,
+ size: 14 }
+ matched: 4
+
+- name: assert actual == expected
+ assert:
+ that: "actual == expected"
+
+- name: find hidden files
+ win_find:
+ paths: ['{{win_find_dir}}\\single', '{{win_find_dir}}\\nested']
+ hidden: True
+ register: actual
+
+- name: set fact for hidden files
+ set_fact:
+ expected:
+ changed: False
+ examined: 11
+ files:
+ - { isarchive: True,
+ attributes: "Hidden, Archive",
+ checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
+ creationtime: 1477984205,
+ extension: .ps1,
+ filename: hidden.ps1,
+ ishidden: True,
+ isdir: False,
+ islink: False,
+ lastaccesstime: 1477984205,
+ lastwritetime: 1477984205,
+ owner: BUILTIN\Administrators,
+ path: "{{win_find_dir}}\\single\\hidden.ps1",
+ isreadonly: False,
+ isshared: False,
+ size: 14 }
+ matched: 1
+
+- name: assert actual == expected
+ assert:
+ that: "actual == expected"
+
+- name: find file based on pattern
+ win_find:
+ paths: '{{win_find_dir}}\\single'
+ patterns: ['*.log', 'out_*']
+ register: actual_pattern
+
+- name: find file based on pattern regex
+ win_find:
+ paths: '{{win_find_dir}}\\single'
+ patterns: "out_\\d{8}-\\d{6}.log"
+ use_regex: True
+ register: actual_regex
+
+- name: set fact for pattern files
+ set_fact:
+ expected:
+ changed: False
+ examined: 5
+ files:
+ - { isarchive: True,
+ attributes: Archive,
+ checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
+ creationtime: 1477984205,
+ extension: .log,
+ filename: out_20161101-091005.log,
+ ishidden: False,
+ isdir: False,
+ islink: False,
+ lastaccesstime: 1477984205,
+ lastwritetime: 1477984205,
+ owner: BUILTIN\Administrators,
+ path: "{{win_find_dir}}\\single\\out_20161101-091005.log",
+ isreadonly: False,
+ isshared: False,
+ size: 14 }
+ matched: 1
+
+- name: assert actual == expected
+ assert:
+ that:
+ - "actual_pattern == expected"
+ - "actual_regex == expected"
+
+- name: find files with recurse set
+ win_find:
+ paths: "{{win_find_dir}}\\nested"
+ recurse: True
+ patterns: "*.ps1"
+ register: actual
+
+- name: set expected value for files in a nested directory
+ set_fact:
+ expected:
+ changed: False
+ examined: 8
+ files:
+ - { isarchive: True,
+ attributes: Archive,
+ checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
+ creationtime: 1477984205,
+ extension: .ps1,
+ filename: test.ps1,
+ ishidden: False,
+ isdir: False,
+ islink: False,
+ lastaccesstime: 1477984205,
+ lastwritetime: 1477984205,
+ owner: BUILTIN\Administrators,
+ path: "{{win_find_dir}}\\nested\\sub-nest\\test.ps1",
+ isreadonly: False,
+ isshared: False,
+ size: 14 }
+ - { isarchive: True,
+ attributes: Archive,
+ checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
+ creationtime: 1477984205,
+ extension: .ps1,
+ filename: file.ps1,
+ ishidden: False,
+ isdir: False,
+ islink: False,
+ lastaccesstime: 1477984205,
+ lastwritetime: 1477984205,
+ owner: BUILTIN\Administrators,
+ path: "{{win_find_dir}}\\nested\\file.ps1",
+ isreadonly: False,
+ isshared: False,
+ size: 14 }
+ - { isarchive: True,
+ attributes: Archive,
+ checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
+ creationtime: 1477984205,
+ extension: .ps1,
+ filename: test.ps1,
+ ishidden: False,
+ isdir: False,
+ islink: False,
+ lastaccesstime: 1477984205,
+ lastwritetime: 1477984205,
+ owner: BUILTIN\Administrators,
+ path: "{{win_find_dir}}\\nested\\test.ps1",
+ isreadonly: False,
+ isshared: False,
+ size: 14 }
+ matched: 3
+
+- name: assert actual == expected
+ assert:
+ that: "actual == expected"
+
+- name: find files with recurse set and follow links
+ win_find:
+ paths: "{{win_find_dir}}\\nested"
+ recurse: True
+ follow: True
+ patterns: "*.ps1"
+ register: actual
+
+- name: set expected value for files in a nested directory while following links
+ set_fact:
+ expected:
+ changed: False
+ examined: 10
+ files:
+ - { isarchive: True,
+ attributes: Archive,
+ checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
+ creationtime: 1477984205,
+ extension: .ps1,
+ filename: link.ps1,
+ ishidden: False,
+ isdir: False,
+ islink: False,
+ lastaccesstime: 1477984205,
+ lastwritetime: 1477984205,
+ owner: BUILTIN\Administrators,
+ path: "{{win_find_dir}}\\nested\\link\\link.ps1",
+ isreadonly: False,
+ isshared: False,
+ size: 14 }
+ - { isarchive: True,
+ attributes: Archive,
+ checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
+ creationtime: 1477984205,
+ extension: .ps1,
+ filename: test.ps1,
+ ishidden: False,
+ isdir: False,
+ islink: False,
+ lastaccesstime: 1477984205,
+ lastwritetime: 1477984205,
+ owner: BUILTIN\Administrators,
+ path: "{{win_find_dir}}\\nested\\sub-nest\\test.ps1",
+ isreadonly: False,
+ isshared: False,
+ size: 14 }
+ - { isarchive: True,
+ attributes: Archive,
+ checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
+ creationtime: 1477984205,
+ extension: .ps1,
+ filename: file.ps1,
+ ishidden: False,
+ isdir: False,
+ islink: False,
+ lastaccesstime: 1477984205,
+ lastwritetime: 1477984205,
+ owner: BUILTIN\Administrators,
+ path: "{{win_find_dir}}\\nested\\file.ps1",
+ isreadonly: False,
+ isshared: False,
+ size: 14 }
+ - { isarchive: True,
+ attributes: Archive,
+ checksum: 8df33cee3325596517df5bb5aa980cf9c5c1fda3,
+ creationtime: 1477984205,
+ extension: .ps1,
+ filename: test.ps1,
+ ishidden: False,
+ isdir: False,
+ islink: False,
+ lastaccesstime: 1477984205,
+ lastwritetime: 1477984205,
+ owner: BUILTIN\Administrators,
+ path: "{{win_find_dir}}\\nested\\test.ps1",
+ isreadonly: False,
+ isshared: False,
+ size: 14 }
+ matched: 4
+
+- name: assert actual == expected
+ assert:
+ that: "actual == expected"
+
+- name: find directories
+ win_find:
+ paths: "{{win_find_dir}}\\link-dest"
+ file_type: directory
+ register: actual
+
+- name: set expected fact for directories with recurse and follow
+ set_fact:
+ expected:
+ changed: False
+ examined: 2
+ files:
+ - { isarchive: False,
+ attributes: Directory,
+ creationtime: 1477984205,
+ filename: sub-link,
+ ishidden: False,
+ isdir: True,
+ islink: False,
+ lastaccesstime: 1477984205,
+ lastwritetime: 1477984205,
+ owner: BUILTIN\Administrators,
+ path: "{{win_find_dir}}\\link-dest\\sub-link",
+ isreadonly: False,
+ isshared: False,
+ size: 0 }
+ matched: 1
+
+- name: assert actual == expected
+ assert:
+ that: "actual == expected"
+
+- name: find directories recurse and follow with a broken link
+ win_find:
+ paths: "{{win_find_dir}}"
+ file_type: directory
+ recurse: True
+ follow: True
+ register: actual
+
+- name: check directory count with recurse and follow is correct
+ assert:
+ that:
+ - "actual.examined == 33"
+ - "actual.matched == 13"
+ - "actual.files[0].filename == 'broken-link'"
+ - "actual.files[0].islink == True"
+ - "actual.files[2].filename == 'junction-link'"
+ - "actual.files[2].islink == True"
+ - "actual.files[2].lnk_source == '{{win_find_dir|regex_replace('\\\\', '\\\\\\\\')}}\\\\junction-link-dest'"
+ - "actual.files[7].filename == 'link'"
+ - "actual.files[7].islink == True"
+ - "actual.files[7].lnk_source == '{{win_find_dir|regex_replace('\\\\', '\\\\\\\\')}}\\\\link-dest'"
+ - "actual.files[11].filename == 'folder'"
+ - "actual.files[11].islink == False"
+ - "actual.files[11].isshared == True"
+ - "actual.files[11].sharename == 'folder-share'"
+
+- name: filter files by size without byte specified
+ win_find:
+ paths: "{{win_find_dir}}\\single"
+ size: 260002
+ register: actual_without_byte
+
+- name: filter files by size with byte specified
+ win_find:
+ paths: "{{win_find_dir}}\\single"
+ size: 253k
+ register: actual_with_byte
+
+- name: set expected fact for files by size
+ set_fact:
+ expected:
+ changed: False
+ examined: 5
+ files:
+ - { isarchive: True,
+ attributes: Archive,
+ checksum: f8d100cdcf0e6c1007db2f8dd0b7ee2884df89af,
+ creationtime: 1477984205,
+ extension: ".ps1",
+ filename: large.ps1,
+ ishidden: False,
+ isdir: False,
+ islink: False,
+ lastaccesstime: 1477984205,
+ lastwritetime: 1477984205,
+ owner: BUILTIN\Administrators,
+ path: "{{win_find_dir}}\\single\\large.ps1",
+ isreadonly: False,
+ isshared: False,
+ size: 260002 }
+ matched: 1
+
+- name: assert actual == expected
+ assert:
+ that:
+ - "actual_without_byte == expected"
+ - "actual_with_byte == expected"
+
+- name: filter files by size (less than) without byte specified
+ win_find:
+ paths: "{{win_find_dir}}\\single"
+ size: -4
+ register: actual_without_byte
+
+- name: filter files by size (less than) with byte specified
+ win_find:
+ paths: "{{win_find_dir}}\\single"
+ size: -4b
+ register: actual_with_byte
+
+- name: set expected fact for files by size (less than)
+ set_fact:
+ expected:
+ changed: False
+ examined: 5
+ files:
+ - { isarchive: True,
+ attributes: Archive,
+ checksum: 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8,
+ creationtime: 1477984205,
+ extension: ".ps1",
+ filename: small.ps1,
+ ishidden: False,
+ isdir: False,
+ islink: False,
+ lastaccesstime: 1477984205,
+ lastwritetime: 1477984205,
+ owner: BUILTIN\Administrators,
+ path: "{{win_find_dir}}\\single\\small.ps1",
+ isreadonly: False,
+ isshared: False,
+ size: 1 }
+ matched: 1
+
+- name: assert actual == expected
+ assert:
+ that:
+ - "actual_without_byte == expected"
+ - "actual_with_byte == expected"
+
+# For dates we cannot assert against expected as the times change, this is a poor mans attempt at testing
+- name: filter files by age without unit specified
+ win_find:
+ paths: "{{win_find_dir}}\\date"
+ age: 3600
+ register: actual_without_unit
+
+- name: filter files by age with unit specified
+ win_find:
+ paths: "{{win_find_dir}}\\date"
+ age: 1h
+ register: actual_with_unit
+
+- name: assert dates match each other
+ assert:
+ that:
+ - "actual_without_unit == actual_with_unit"
+ - "actual_without_unit.matched == 1"
+ - "actual_without_unit.files[0].checksum == '7454f04e3ac587f711a416f4edf26507255e0a2e'"
+ - "actual_without_unit.files[0].path == '{{win_find_dir|regex_replace('\\\\', '\\\\\\\\')}}\\\\date\\\\new.ps1'"
+
+- name: filter files by age (older than) without unit specified
+ win_find:
+ paths: "{{win_find_dir}}\\date"
+ age: -1
+ register: actual_without_unit
+
+- name: filter files by age (older than) without unit specified
+ win_find:
+ paths: "{{win_find_dir}}\\date"
+ age: -1s
+ register: actual_with_unit
+
+- name: assert dates match each other
+ assert:
+ that:
+ - "actual_without_unit == actual_with_unit"
+ - "actual_without_unit.matched == 2"
+ - "actual_without_unit.files[0].checksum == '7454f04e3ac587f711a416f4edf26507255e0a2e'"
+ - "actual_without_unit.files[0].path == '{{win_find_dir|regex_replace('\\\\', '\\\\\\\\')}}\\\\date\\\\new.ps1'"
+ - "actual_without_unit.files[1].checksum == '031a04ecc76f794d7842651de732075dec6fef04'"
+ - "actual_without_unit.files[1].path == '{{win_find_dir|regex_replace('\\\\', '\\\\\\\\')}}\\\\date\\\\old.ps1'"
+
+- name: get list of files with md5 checksum
+ win_find:
+ paths: "{{win_find_dir}}\\single"
+ patterns: test.ps1
+ checksum_algorithm: md5
+ register: actual_md5_checksum
+
+- name: assert md5 checksum value
+ assert:
+ that:
+ - "actual_md5_checksum.files[0].checksum == 'd1713d0f1d2e8fae230328d8fd59de01'"
+
+- name: get list of files with sha1 checksum
+ win_find:
+ paths: "{{win_find_dir}}\\single"
+ patterns: test.ps1
+ checksum_algorithm: sha1
+ register: actual_sha1_checksum
+
+- name: assert sha1 checksum value
+ assert:
+ that:
+ - "actual_sha1_checksum.files[0].checksum == '8df33cee3325596517df5bb5aa980cf9c5c1fda3'"
+
+- name: get list of files with sha256 checksum
+ win_find:
+ paths: "{{win_find_dir}}\\single"
+ patterns: test.ps1
+ checksum_algorithm: sha256
+ register: actual_sha256_checksum
+
+- name: assert sha256 checksum value
+ assert:
+ that:
+ - "actual_sha256_checksum.files[0].checksum == 'c20d2eba7ffda0079812721b6f4e4e109e2f0c5e8cc3d1273a060df6f7d9f339'"
+
+- name: get list of files with sha384 checksum
+ win_find:
+ paths: "{{win_find_dir}}\\single"
+ patterns: test.ps1
+ checksum_algorithm: sha384
+ register: actual_sha384_checksum
+
+- name: assert sha384 checksum value
+ assert:
+ that:
+ - "actual_sha384_checksum.files[0].checksum == 'aed515eb216b9c7009ae8c4680f46c1e22004528b231aa0482a8587543bca47d3504e9f77e884eb2d11b2f9f5dc01651'"
+
+- name: get list of files with sha512 checksum
+ win_find:
+ paths: "{{win_find_dir}}\\single"
+ patterns: test.ps1
+ checksum_algorithm: sha512
+ register: actual_sha512_checksum
+
+- name: assert sha512 checksum value
+ assert:
+ that:
+ - "actual_sha512_checksum.files[0].checksum == '05abf64a68c4731699c23b4fc6894a36646fce525f3c96f9cf743b5d0c3bfd933dad0e95e449e3afe1f74d534d69a53b8f46cf835763dd42915813c897b02b87'"
+
+- name: get list of files without checksum
+ win_find:
+ paths: "{{win_find_dir}}\\single"
+ patterns: test.ps1
+ get_checksum: False
+ register: actual_no_checksum
+
+- name: assert no checksum is returned
+ assert:
+ that:
+ - "actual_no_checksum.files[0].checksum is undefined"
+
+- name: check if broken symbolic link exists
+ win_stat:
+ path: "{{win_find_dir}}\\broken-link"
+ register: broken_link_exists
+
+- name: delete broken symbolic link if it exists
+ win_command: cmd.exe /c rmdir {{win_find_dir}}\broken-link
+
+ when: broken_link_exists.stat.exists
+
+- name: check if junction symbolic link exists
+ win_stat:
+ path: "{{win_find_dir}}\\junction-link"
+ register: junction_link_exists
+
+- name: delete junction symbolic link if it exists
+ win_command: cmd.exe /c rmdir {{win_find_dir}}\junction-link
+ when: junction_link_exists.stat.exists
+
+- name: check if nested symbolic link exists
+ win_stat:
+ path: "{{win_find_dir}}\\nested\\link"
+ register: nested_link_exists
+
+- name: delete nested symbolic link if it exists
+ win_command: cmd.exe /c rmdir {{win_find_dir}}\nested\link
+ when: nested_link_exists.stat.exists
+
+- name: remove testing folder
+ win_file:
+ path: "{{win_find_dir}}"
+ state: absent
diff --git a/test/integration/test_win_group2.yml b/test/integration/test_win_group2.yml
index 27765ea599..5eae464ec3 100644
--- a/test/integration/test_win_group2.yml
+++ b/test/integration/test_win_group2.yml
@@ -7,6 +7,7 @@
- { role: win_template, tags: test_win_template }
- { role: win_lineinfile, tags: test_win_lineinfile }
- { role: win_stat, tags: test_win_stat }
+ - { role: win_find, tags: test_win_find }
- { role: win_get_url, tags: test_win_get_url }
- { role: win_msi, tags: test_win_msi }
- { role: win_package, tags: test_win_package }