mirror of https://github.com/ansible/ansible.git
ansible-test pssa update and new rules (#76256)
parent
90de24da7b
commit
9985b8a975
@ -0,0 +1,17 @@
|
||||
minor_changes:
|
||||
- ansible-test pslint - Updated ``PowerShellScriptAnalyzer`` to 1.20.0
|
||||
- >-
|
||||
ansible-test pslint - Added the `AvoidLongLines <https://github.com/PowerShell/PSScriptAnalyzer/blob/master/docs/Rules/AvoidLongLines.md>`_
|
||||
rule set to a length of 160.
|
||||
- >-
|
||||
ansible-test pslint - Added the `PlaceOpenBrace <https://github.com/PowerShell/PSScriptAnalyzer/blob/master/docs/Rules/PlaceOpenBrace.md>`_
|
||||
rule set to enforce open braces on the same line and a subsequent newline.
|
||||
- >-
|
||||
ansible-test pslint - Added the `PlaceCloseBrace <https://github.com/PowerShell/PSScriptAnalyzer/blob/master/docs/Rules/PlaceCloseBrace.md>`_
|
||||
rule set to enforce close braces on a newline.
|
||||
- >-
|
||||
ansible-test pslint - Added the `UseConsistentIndentation <https://github.com/PowerShell/PSScriptAnalyzer/blob/master/docs/Rules/UseConsistentIndentation.md>`_
|
||||
rule to enforce indentation is done with 4 spaces.
|
||||
- >-
|
||||
ansible-test pslint - Added the `UseConsistentWhitespace <https://github.com/PowerShell/PSScriptAnalyzer/blob/master/docs/Rules/UseConsistentWhitespace.md>`_
|
||||
rule to enforce whitespace consistency in PowerShell.
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
#!powershell
|
||||
# POWERSHELL_COMMON
|
||||
|
||||
Exit-Json @{ data="success" }
|
||||
Exit-Json @{ data = "success" }
|
||||
|
@ -1,3 +1,3 @@
|
||||
# Test script to create a file.
|
||||
|
||||
echo $null > $args[0]
|
||||
Write-Output $null > $args[0]
|
||||
|
@ -1,7 +1,6 @@
|
||||
# Test script to make sure the Ansible script module works when arguments are
|
||||
# passed to the script.
|
||||
|
||||
foreach ($i in $args)
|
||||
{
|
||||
foreach ($i in $args) {
|
||||
Write-Host $i;
|
||||
}
|
||||
|
@ -1,129 +0,0 @@
|
||||
#!powershell
|
||||
|
||||
# Copyright: 2019, rnsc(@rnsc) <github@rnsc.be>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
|
||||
#AnsibleRequires -CSharpUtil Ansible.Basic
|
||||
#AnsibleRequires -OSVersion 6.3
|
||||
|
||||
$spec = @{
|
||||
options = @{
|
||||
drive_letter = @{ type = "str"; required = $true }
|
||||
state = @{ type = "str"; choices = "absent", "present"; default = "present"; }
|
||||
settings = @{
|
||||
type = "dict"
|
||||
required = $false
|
||||
options = @{
|
||||
minimum_file_size = @{ type = "int"; default = 32768 }
|
||||
minimum_file_age_days = @{ type = "int"; default = 2 }
|
||||
no_compress = @{ type = "bool"; required = $false; default = $false }
|
||||
optimize_in_use_files = @{ type = "bool"; required = $false; default = $false }
|
||||
verify = @{ type = "bool"; required = $false; default = $false }
|
||||
}
|
||||
}
|
||||
}
|
||||
supports_check_mode = $true
|
||||
}
|
||||
|
||||
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
|
||||
|
||||
$drive_letter = $module.Params.drive_letter
|
||||
$state = $module.Params.state
|
||||
$settings = $module.Params.settings
|
||||
|
||||
$module.Result.changed = $false
|
||||
$module.Result.reboot_required = $false
|
||||
$module.Result.msg = ""
|
||||
|
||||
function Set-DataDeduplication($volume, $state, $settings, $dedup_job) {
|
||||
|
||||
$current_state = 'absent'
|
||||
|
||||
try {
|
||||
$dedup_info = Get-DedupVolume -Volume "$($volume.DriveLetter):"
|
||||
} catch {
|
||||
$dedup_info = $null
|
||||
}
|
||||
|
||||
if ($dedup_info.Enabled) {
|
||||
$current_state = 'present'
|
||||
}
|
||||
|
||||
if ( $state -ne $current_state ) {
|
||||
if( -not $module.CheckMode) {
|
||||
if($state -eq 'present') {
|
||||
# Enable-DedupVolume -Volume <String>
|
||||
Enable-DedupVolume -Volume "$($volume.DriveLetter):"
|
||||
} elseif ($state -eq 'absent') {
|
||||
Disable-DedupVolume -Volume "$($volume.DriveLetter):"
|
||||
}
|
||||
}
|
||||
$module.Result.changed = $true
|
||||
}
|
||||
|
||||
if ($state -eq 'present') {
|
||||
if ($null -ne $settings) {
|
||||
Set-DataDedupJobSettings -volume $volume -settings $settings
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Set-DataDedupJobSettings ($volume, $settings) {
|
||||
|
||||
try {
|
||||
$dedup_info = Get-DedupVolume -Volume "$($volume.DriveLetter):"
|
||||
} catch {
|
||||
$dedup_info = $null
|
||||
}
|
||||
|
||||
ForEach ($key in $settings.keys) {
|
||||
|
||||
# See Microsoft documentation:
|
||||
# https://docs.microsoft.com/en-us/powershell/module/deduplication/set-dedupvolume?view=win10-ps
|
||||
|
||||
$update_key = $key
|
||||
$update_value = $settings.$($key)
|
||||
# Transform Ansible style options to Powershell params
|
||||
$update_key = $update_key -replace('_', '')
|
||||
|
||||
if ($update_key -eq "MinimumFileSize" -and $update_value -lt 32768) {
|
||||
$update_value = 32768
|
||||
}
|
||||
|
||||
$current_value = ($dedup_info | Select-Object -ExpandProperty $update_key)
|
||||
|
||||
if ($update_value -ne $current_value) {
|
||||
$command_param = @{
|
||||
$($update_key) = $update_value
|
||||
}
|
||||
|
||||
# Set-DedupVolume -Volume <String>`
|
||||
# -NoCompress <bool> `
|
||||
# -MinimumFileAgeDays <UInt32> `
|
||||
# -MinimumFileSize <UInt32> (minimum 32768)
|
||||
if( -not $module.CheckMode ) {
|
||||
Set-DedupVolume -Volume "$($volume.DriveLetter):" @command_param
|
||||
}
|
||||
|
||||
$module.Result.changed = $true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Install required feature
|
||||
$feature_name = "FS-Data-Deduplication"
|
||||
if( -not $module.CheckMode) {
|
||||
$feature = Install-WindowsFeature -Name $feature_name
|
||||
|
||||
if ($feature.RestartNeeded -eq 'Yes') {
|
||||
$module.Result.reboot_required = $true
|
||||
$module.FailJson("$feature_name was installed but requires Windows to be rebooted to work.")
|
||||
}
|
||||
}
|
||||
|
||||
$volume = Get-Volume -DriveLetter $drive_letter
|
||||
|
||||
Set-DataDeduplication -volume $volume -state $state -settings $settings -dedup_job $dedup_job
|
||||
|
||||
$module.ExitJson()
|
@ -1,87 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: 2019, rnsc(@rnsc) <github@rnsc.be>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: win_data_deduplication
|
||||
version_added: "2.10"
|
||||
short_description: Module to enable Data Deduplication on a volume.
|
||||
description:
|
||||
- This module can be used to enable Data Deduplication on a Windows volume.
|
||||
- The module will install the FS-Data-Deduplication feature (a reboot will be necessary).
|
||||
options:
|
||||
drive_letter:
|
||||
description:
|
||||
- Windows drive letter on which to enable data deduplication.
|
||||
required: yes
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Wether to enable or disable data deduplication on the selected volume.
|
||||
default: present
|
||||
type: str
|
||||
choices: [ present, absent ]
|
||||
settings:
|
||||
description:
|
||||
- Dictionary of settings to pass to the Set-DedupVolume powershell command.
|
||||
type: dict
|
||||
suboptions:
|
||||
minimum_file_size:
|
||||
description:
|
||||
- Minimum file size you want to target for deduplication.
|
||||
- It will default to 32768 if not defined or if the value is less than 32768.
|
||||
type: int
|
||||
default: 32768
|
||||
minimum_file_age_days:
|
||||
description:
|
||||
- Minimum file age you want to target for deduplication.
|
||||
type: int
|
||||
default: 2
|
||||
no_compress:
|
||||
description:
|
||||
- Wether you want to enabled filesystem compression or not.
|
||||
type: bool
|
||||
default: no
|
||||
optimize_in_use_files:
|
||||
description:
|
||||
- Indicates that the server attempts to optimize currently open files.
|
||||
type: bool
|
||||
default: no
|
||||
verify:
|
||||
description:
|
||||
- Indicates whether the deduplication engine performs a byte-for-byte verification for each duplicate chunk
|
||||
that optimization creates, rather than relying on a cryptographically strong hash.
|
||||
- This option is not recommend.
|
||||
- Setting this parameter to True can degrade optimization performance.
|
||||
type: bool
|
||||
default: no
|
||||
author:
|
||||
- rnsc (@rnsc)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Enable Data Deduplication on D
|
||||
win_data_deduplication:
|
||||
drive_letter: 'D'
|
||||
state: present
|
||||
|
||||
- name: Enable Data Deduplication on D
|
||||
win_data_deduplication:
|
||||
drive_letter: 'D'
|
||||
state: present
|
||||
settings:
|
||||
no_compress: true
|
||||
minimum_file_age_days: 1
|
||||
minimum_file_size: 0
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
#
|
||||
'''
|
@ -1,398 +0,0 @@
|
||||
#!powershell
|
||||
|
||||
# Copyright: (c) 2015, Trond Hindenes <trond@hindenes.com>, and others
|
||||
# Copyright: (c) 2017, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#AnsibleRequires -CSharpUtil Ansible.Basic
|
||||
#Requires -Version 5
|
||||
|
||||
Function ConvertTo-ArgSpecType {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Converts the DSC parameter type to the arg spec type required for Ansible.
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][String]$CimType
|
||||
)
|
||||
|
||||
$arg_type = switch($CimType) {
|
||||
Boolean { "bool" }
|
||||
Char16 { [Func[[Object], [Char]]]{ [System.Char]::Parse($args[0].ToString()) } }
|
||||
DateTime { [Func[[Object], [DateTime]]]{ [System.DateTime]($args[0].ToString()) } }
|
||||
Instance { "dict" }
|
||||
Real32 { "float" }
|
||||
Real64 { [Func[[Object], [Double]]]{ [System.Double]::Parse($args[0].ToString()) } }
|
||||
Reference { "dict" }
|
||||
SInt16 { [Func[[Object], [Int16]]]{ [System.Int16]::Parse($args[0].ToString()) } }
|
||||
SInt32 { "int" }
|
||||
SInt64 { [Func[[Object], [Int64]]]{ [System.Int64]::Parse($args[0].ToString()) } }
|
||||
SInt8 { [Func[[Object], [SByte]]]{ [System.SByte]::Parse($args[0].ToString()) } }
|
||||
String { "str" }
|
||||
UInt16 { [Func[[Object], [UInt16]]]{ [System.UInt16]::Parse($args[0].ToString()) } }
|
||||
UInt32 { [Func[[Object], [UInt32]]]{ [System.UInt32]::Parse($args[0].ToString()) } }
|
||||
UInt64 { [Func[[Object], [UInt64]]]{ [System.UInt64]::Parse($args[0].ToString()) } }
|
||||
UInt8 { [Func[[Object], [Byte]]]{ [System.Byte]::Parse($args[0].ToString()) } }
|
||||
Unknown { "raw" }
|
||||
default { "raw" }
|
||||
}
|
||||
return $arg_type
|
||||
}
|
||||
|
||||
Function Get-DscCimClassProperties {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get's a list of CimProperties of a CIM Class. It filters out any magic or
|
||||
read only properties that we don't need to know about.
|
||||
#>
|
||||
param([Parameter(Mandatory=$true)][String]$ClassName)
|
||||
|
||||
$resource = Get-CimClass -ClassName $ClassName -Namespace root\Microsoft\Windows\DesiredStateConfiguration
|
||||
|
||||
# Filter out any magic properties that are used internally on an OMI_BaseResource
|
||||
# https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/DscSupport/CimDSCParser.cs#L1203
|
||||
$magic_properties = @("ResourceId", "SourceInfo", "ModuleName", "ModuleVersion", "ConfigurationName")
|
||||
$properties = $resource.CimClassProperties | Where-Object {
|
||||
|
||||
($resource.CimSuperClassName -ne "OMI_BaseResource" -or $_.Name -notin $magic_properties) -and
|
||||
-not $_.Flags.HasFlag([Microsoft.Management.Infrastructure.CimFlags]::ReadOnly)
|
||||
}
|
||||
|
||||
return ,$properties
|
||||
}
|
||||
|
||||
Function Add-PropertyOption {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Adds the spec for the property type to the existing module specification.
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][Hashtable]$Spec,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[Microsoft.Management.Infrastructure.CimPropertyDeclaration]$Property
|
||||
)
|
||||
|
||||
$option = @{
|
||||
required = $false
|
||||
}
|
||||
$property_name = $Property.Name
|
||||
$property_type = $Property.CimType.ToString()
|
||||
|
||||
if ($Property.Flags.HasFlag([Microsoft.Management.Infrastructure.CimFlags]::Key) -or
|
||||
$Property.Flags.HasFlag([Microsoft.Management.Infrastructure.CimFlags]::Required)) {
|
||||
$option.required = $true
|
||||
}
|
||||
|
||||
if ($null -ne $Property.Qualifiers['Values']) {
|
||||
$option.choices = [System.Collections.Generic.List`1[Object]]$Property.Qualifiers['Values'].Value
|
||||
}
|
||||
|
||||
if ($property_name -eq "Name") {
|
||||
# For backwards compatibility we support specifying the Name DSC property as item_name
|
||||
$option.aliases = @("item_name")
|
||||
} elseif ($property_name -ceq "key") {
|
||||
# There seems to be a bug in the CIM property parsing when the property name is 'Key'. The CIM instance will
|
||||
# think the name is 'key' when the MOF actually defines it as 'Key'. We set the proper casing so the module arg
|
||||
# validator won't fire a case sensitive warning
|
||||
$property_name = "Key"
|
||||
}
|
||||
|
||||
if ($Property.ReferenceClassName -eq "MSFT_Credential") {
|
||||
# Special handling for the MSFT_Credential type (PSCredential), we handle this with having 2 options that
|
||||
# have the suffix _username and _password.
|
||||
$option_spec_pass = @{
|
||||
type = "str"
|
||||
required = $option.required
|
||||
no_log = $true
|
||||
}
|
||||
$Spec.options."$($property_name)_password" = $option_spec_pass
|
||||
$Spec.required_together.Add(@("$($property_name)_username", "$($property_name)_password")) > $null
|
||||
|
||||
$property_name = "$($property_name)_username"
|
||||
$option.type = "str"
|
||||
} elseif ($Property.ReferenceClassName -eq "MSFT_KeyValuePair") {
|
||||
$option.type = "dict"
|
||||
} elseif ($property_type.EndsWith("Array")) {
|
||||
$option.type = "list"
|
||||
$option.elements = ConvertTo-ArgSpecType -CimType $property_type.Substring(0, $property_type.Length - 5)
|
||||
} else {
|
||||
$option.type = ConvertTo-ArgSpecType -CimType $property_type
|
||||
}
|
||||
|
||||
if (($option.type -eq "dict" -or ($option.type -eq "list" -and $option.elements -eq "dict")) -and
|
||||
$Property.ReferenceClassName -ne "MSFT_KeyValuePair") {
|
||||
# Get the sub spec if the type is a Instance (CimInstance/dict)
|
||||
$sub_option_spec = Get-OptionSpec -ClassName $Property.ReferenceClassName
|
||||
$option += $sub_option_spec
|
||||
}
|
||||
|
||||
$Spec.options.$property_name = $option
|
||||
}
|
||||
|
||||
Function Get-OptionSpec {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Generates the specifiec used in AnsibleModule for a CIM MOF resource name.
|
||||
|
||||
.NOTES
|
||||
This won't be able to retrieve the default values for an option as that is not defined in the MOF for a resource.
|
||||
Default values are still preserved in the DSC engine if we don't pass in the property at all, we just can't report
|
||||
on what they are automatically.
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][String]$ClassName
|
||||
)
|
||||
|
||||
$spec = @{
|
||||
options = @{}
|
||||
required_together = [System.Collections.ArrayList]@()
|
||||
}
|
||||
$properties = Get-DscCimClassProperties -ClassName $ClassName
|
||||
foreach ($property in $properties) {
|
||||
Add-PropertyOption -Spec $spec -Property $property
|
||||
}
|
||||
|
||||
return $spec
|
||||
}
|
||||
|
||||
Function ConvertTo-CimInstance {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Converts a dict to a CimInstance of the specified Class. Also provides a
|
||||
better error message if this fails that contains the option name that failed.
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][String]$Name,
|
||||
[Parameter(Mandatory=$true)][String]$ClassName,
|
||||
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Value,
|
||||
[Parameter(Mandatory=$true)][Ansible.Basic.AnsibleModule]$Module,
|
||||
[Switch]$Recurse
|
||||
)
|
||||
|
||||
$properties = @{}
|
||||
foreach ($value_info in $Value.GetEnumerator()) {
|
||||
# Need to remove all null values from existing dict so the conversion works
|
||||
if ($null -eq $value_info.Value) {
|
||||
continue
|
||||
}
|
||||
$properties.($value_info.Key) = $value_info.Value
|
||||
}
|
||||
|
||||
if ($Recurse) {
|
||||
# We want to validate and convert and values to what's required by DSC
|
||||
$properties = ConvertTo-DscProperty -ClassName $ClassName -Params $properties -Module $Module
|
||||
}
|
||||
|
||||
try {
|
||||
return (New-CimInstance -ClassName $ClassName -Property $properties -ClientOnly)
|
||||
} catch {
|
||||
# New-CimInstance raises a poor error message, make sure we mention what option it is for
|
||||
$Module.FailJson("Failed to cast dict value for option '$Name' to a CimInstance: $($_.Exception.Message)", $_)
|
||||
}
|
||||
}
|
||||
|
||||
Function ConvertTo-DscProperty {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Converts the input module parameters that have been validated and casted
|
||||
into the types expected by the DSC engine. This is mostly done to deal with
|
||||
types like PSCredential and Dictionaries.
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][String]$ClassName,
|
||||
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Params,
|
||||
[Parameter(Mandatory=$true)][Ansible.Basic.AnsibleModule]$Module
|
||||
)
|
||||
$properties = Get-DscCimClassProperties -ClassName $ClassName
|
||||
|
||||
$dsc_properties = @{}
|
||||
foreach ($property in $properties) {
|
||||
$property_name = $property.Name
|
||||
$property_type = $property.CimType.ToString()
|
||||
|
||||
if ($property.ReferenceClassName -eq "MSFT_Credential") {
|
||||
$username = $Params."$($property_name)_username"
|
||||
$password = $Params."$($property_name)_password"
|
||||
|
||||
# No user set == No option set in playbook, skip this property
|
||||
if ($null -eq $username) {
|
||||
continue
|
||||
}
|
||||
$sec_password = ConvertTo-SecureString -String $password -AsPlainText -Force
|
||||
$value = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $sec_password
|
||||
} else {
|
||||
$value = $Params.$property_name
|
||||
|
||||
# The actual value wasn't set, skip adding this property
|
||||
if ($null -eq $value) {
|
||||
continue
|
||||
}
|
||||
|
||||
if ($property.ReferenceClassName -eq "MSFT_KeyValuePair") {
|
||||
$key_value_pairs = [System.Collections.Generic.List`1[CimInstance]]@()
|
||||
foreach ($value_info in $value.GetEnumerator()) {
|
||||
$kvp = @{Key = $value_info.Key; Value = $value_info.Value.ToString()}
|
||||
$cim_instance = ConvertTo-CimInstance -Name $property_name -ClassName MSFT_KeyValuePair `
|
||||
-Value $kvp -Module $Module
|
||||
$key_value_pairs.Add($cim_instance) > $null
|
||||
}
|
||||
$value = $key_value_pairs.ToArray()
|
||||
} elseif ($null -ne $property.ReferenceClassName) {
|
||||
# Convert the dict to a CimInstance (or list of CimInstances)
|
||||
$convert_args = @{
|
||||
ClassName = $property.ReferenceClassName
|
||||
Module = $Module
|
||||
Name = $property_name
|
||||
Recurse = $true
|
||||
}
|
||||
if ($property_type.EndsWith("Array")) {
|
||||
$value = [System.Collections.Generic.List`1[CimInstance]]@()
|
||||
foreach ($raw in $Params.$property_name.GetEnumerator()) {
|
||||
$cim_instance = ConvertTo-CimInstance -Value $raw @convert_args
|
||||
$value.Add($cim_instance) > $null
|
||||
}
|
||||
$value = $value.ToArray() # Need to make sure we are dealing with an Array not a List
|
||||
} else {
|
||||
$value = ConvertTo-CimInstance -Value $value @convert_args
|
||||
}
|
||||
}
|
||||
}
|
||||
$dsc_properties.$property_name = $value
|
||||
}
|
||||
|
||||
return $dsc_properties
|
||||
}
|
||||
|
||||
Function Invoke-DscMethod {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Invokes the DSC Resource Method specified in another PS pipeline. This is
|
||||
done so we can retrieve the Verbose stream and return it back to the user
|
||||
for futher debugging.
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][Ansible.Basic.AnsibleModule]$Module,
|
||||
[Parameter(Mandatory=$true)][String]$Method,
|
||||
[Parameter(Mandatory=$true)][Hashtable]$Arguments
|
||||
)
|
||||
|
||||
# Invoke the DSC resource in a separate runspace so we can capture the Verbose output
|
||||
$ps = [PowerShell]::Create()
|
||||
$ps.AddCommand("Invoke-DscResource").AddParameter("Method", $Method) > $null
|
||||
$ps.AddParameters($Arguments) > $null
|
||||
|
||||
$result = $ps.Invoke()
|
||||
|
||||
# Pass the warnings through to the AnsibleModule return result
|
||||
foreach ($warning in $ps.Streams.Warning) {
|
||||
$Module.Warn($warning.Message)
|
||||
}
|
||||
|
||||
# If running at a high enough verbosity, add the verbose output to the AnsibleModule return result
|
||||
if ($Module.Verbosity -ge 3) {
|
||||
$verbose_logs = [System.Collections.Generic.List`1[String]]@()
|
||||
foreach ($verbosity in $ps.Streams.Verbose) {
|
||||
$verbose_logs.Add($verbosity.Message) > $null
|
||||
}
|
||||
$Module.Result."verbose_$($Method.ToLower())" = $verbose_logs
|
||||
}
|
||||
|
||||
if ($ps.HadErrors) {
|
||||
# Cannot pass in the ErrorRecord as it's a RemotingErrorRecord and doesn't contain the ScriptStackTrace
|
||||
# or other info that would be useful
|
||||
$Module.FailJson("Failed to invoke DSC $Method method: $($ps.Streams.Error[0].Exception.Message)")
|
||||
}
|
||||
|
||||
return $result
|
||||
}
|
||||
|
||||
# win_dsc is unique in that is builds the arg spec based on DSC Resource input. To get this info
|
||||
# we need to read the resource_name and module_version value which is done outside of Ansible.Basic
|
||||
if ($args.Length -gt 0) {
|
||||
$params = Get-Content -Path $args[0] | ConvertFrom-Json
|
||||
} else {
|
||||
$params = $complex_args
|
||||
}
|
||||
if (-not $params.ContainsKey("resource_name")) {
|
||||
$res = @{
|
||||
msg = "missing required argument: resource_name"
|
||||
failed = $true
|
||||
}
|
||||
Write-Output -InputObject (ConvertTo-Json -Compress -InputObject $res)
|
||||
exit 1
|
||||
}
|
||||
$resource_name = $params.resource_name
|
||||
|
||||
if ($params.ContainsKey("module_version")) {
|
||||
$module_version = $params.module_version
|
||||
} else {
|
||||
$module_version = "latest"
|
||||
}
|
||||
|
||||
$module_versions = (Get-DscResource -Name $resource_name -ErrorAction SilentlyContinue | Sort-Object -Property Version)
|
||||
$resource = $null
|
||||
if ($module_version -eq "latest" -and $null -ne $module_versions) {
|
||||
$resource = $module_versions[-1]
|
||||
} elseif ($module_version -ne "latest") {
|
||||
$resource = $module_versions | Where-Object { $_.Version -eq $module_version }
|
||||
}
|
||||
|
||||
if (-not $resource) {
|
||||
if ($module_version -eq "latest") {
|
||||
$msg = "Resource '$resource_name' not found."
|
||||
} else {
|
||||
$msg = "Resource '$resource_name' with version '$module_version' not found."
|
||||
$msg += " Versions installed: '$($module_versions.Version -join "', '")'."
|
||||
}
|
||||
|
||||
Write-Output -InputObject (ConvertTo-Json -Compress -InputObject @{ failed = $true; msg = $msg })
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Build the base args for the DSC Invocation based on the resource selected
|
||||
$dsc_args = @{
|
||||
Name = $resource.Name
|
||||
}
|
||||
|
||||
# Binary resources are not working very well with that approach - need to guesstimate module name/version
|
||||
$module_version = $null
|
||||
if ($resource.Module) {
|
||||
$dsc_args.ModuleName = @{
|
||||
ModuleName = $resource.Module.Name
|
||||
ModuleVersion = $resource.Module.Version
|
||||
}
|
||||
$module_version = $resource.Module.Version.ToString()
|
||||
} else {
|
||||
$dsc_args.ModuleName = "PSDesiredStateConfiguration"
|
||||
}
|
||||
|
||||
# To ensure the class registered with CIM is the one based on our version, we want to run the Get method so the DSC
|
||||
# engine updates the metadata propery. We don't care about any errors here
|
||||
try {
|
||||
Invoke-DscResource -Method Get -Property @{Fake="Fake"} @dsc_args > $null
|
||||
} catch {}
|
||||
|
||||
# Dynamically build the option spec based on the resource_name specified and create the module object
|
||||
$spec = Get-OptionSpec -ClassName $resource.ResourceType
|
||||
$spec.supports_check_mode = $true
|
||||
$spec.options.module_version = @{ type = "str"; default = "latest" }
|
||||
$spec.options.resource_name = @{ type = "str"; required = $true }
|
||||
|
||||
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
|
||||
$module.Result.reboot_required = $false
|
||||
$module.Result.module_version = $module_version
|
||||
|
||||
# Build the DSC invocation arguments and invoke the resource
|
||||
$dsc_args.Property = ConvertTo-DscProperty -ClassName $resource.ResourceType -Module $module -Params $Module.Params
|
||||
$dsc_args.Verbose = $true
|
||||
|
||||
$test_result = Invoke-DscMethod -Module $module -Method Test -Arguments $dsc_args
|
||||
if ($test_result.InDesiredState -ne $true) {
|
||||
if (-not $module.CheckMode) {
|
||||
$result = Invoke-DscMethod -Module $module -Method Set -Arguments $dsc_args
|
||||
$module.Result.reboot_required = $result.RebootRequired
|
||||
}
|
||||
$module.Result.changed = $true
|
||||
}
|
||||
|
||||
$module.ExitJson()
|
@ -1,183 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2015, Trond Hindenes <trond@hindenes.com>, and others
|
||||
# Copyright: (c) 2017, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: win_dsc
|
||||
version_added: "2.4"
|
||||
short_description: Invokes a PowerShell DSC configuration
|
||||
description:
|
||||
- Configures a resource using PowerShell DSC.
|
||||
- Requires PowerShell version 5.0 or newer.
|
||||
- Most of the options for this module are dynamic and will vary depending on
|
||||
the DSC Resource specified in I(resource_name).
|
||||
- See :doc:`/user_guide/windows_dsc` for more information on how to use this module.
|
||||
options:
|
||||
resource_name:
|
||||
description:
|
||||
- The name of the DSC Resource to use.
|
||||
- Must be accessible to PowerShell using any of the default paths.
|
||||
type: str
|
||||
required: yes
|
||||
module_version:
|
||||
description:
|
||||
- Can be used to configure the exact version of the DSC resource to be
|
||||
invoked.
|
||||
- Useful if the target node has multiple versions installed of the module
|
||||
containing the DSC resource.
|
||||
- If not specified, the module will follow standard PowerShell convention
|
||||
and use the highest version available.
|
||||
type: str
|
||||
default: latest
|
||||
free_form:
|
||||
description:
|
||||
- The M(win_dsc) module takes in multiple free form options based on the
|
||||
DSC resource being invoked by I(resource_name).
|
||||
- There is no option actually named C(free_form) so see the examples.
|
||||
- This module will try and convert the option to the correct type required
|
||||
by the DSC resource and throw a warning if it fails.
|
||||
- If the type of the DSC resource option is a C(CimInstance) or
|
||||
C(CimInstance[]), this means the value should be a dictionary or list
|
||||
of dictionaries based on the values required by that option.
|
||||
- If the type of the DSC resource option is a C(PSCredential) then there
|
||||
needs to be 2 options set in the Ansible task definition suffixed with
|
||||
C(_username) and C(_password).
|
||||
- If the type of the DSC resource option is an array, then a list should be
|
||||
provided but a comma separated string also work. Use a list where
|
||||
possible as no escaping is required and it works with more complex types
|
||||
list C(CimInstance[]).
|
||||
- If the type of the DSC resource option is a C(DateTime), you should use
|
||||
a string in the form of an ISO 8901 string to ensure the exact date is
|
||||
used.
|
||||
- Since Ansible 2.8, Ansible will now validate the input fields against the
|
||||
DSC resource definition automatically. Older versions will silently
|
||||
ignore invalid fields.
|
||||
type: str
|
||||
required: true
|
||||
notes:
|
||||
- By default there are a few builtin resources that come with PowerShell 5.0,
|
||||
see U(https://docs.microsoft.com/en-us/powershell/scripting/dsc/resources/resources) for
|
||||
more information on these resources.
|
||||
- Custom DSC resources can be installed with M(win_psmodule) using the I(name)
|
||||
option.
|
||||
- The DSC engine run's each task as the SYSTEM account, any resources that need
|
||||
to be accessed with a different account need to have C(PsDscRunAsCredential)
|
||||
set.
|
||||
- To see the valid options for a DSC resource, run the module with C(-vvv) to
|
||||
show the possible module invocation. Default values are not shown in this
|
||||
output but are applied within the DSC engine.
|
||||
author:
|
||||
- Trond Hindenes (@trondhindenes)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Extract zip file
|
||||
win_dsc:
|
||||
resource_name: Archive
|
||||
Ensure: Present
|
||||
Path: C:\Temp\zipfile.zip
|
||||
Destination: C:\Temp\Temp2
|
||||
|
||||
- name: Install a Windows feature with the WindowsFeature resource
|
||||
win_dsc:
|
||||
resource_name: WindowsFeature
|
||||
Name: telnet-client
|
||||
|
||||
- name: Edit HKCU reg key under specific user
|
||||
win_dsc:
|
||||
resource_name: Registry
|
||||
Ensure: Present
|
||||
Key: HKEY_CURRENT_USER\ExampleKey
|
||||
ValueName: TestValue
|
||||
ValueData: TestData
|
||||
PsDscRunAsCredential_username: '{{ansible_user}}'
|
||||
PsDscRunAsCredential_password: '{{ansible_password}}'
|
||||
no_log: true
|
||||
|
||||
- name: Create file with multiple attributes
|
||||
win_dsc:
|
||||
resource_name: File
|
||||
DestinationPath: C:\ansible\dsc
|
||||
Attributes: # can also be a comma separated string, e.g. 'Hidden, System'
|
||||
- Hidden
|
||||
- System
|
||||
Ensure: Present
|
||||
Type: Directory
|
||||
|
||||
- name: Call DSC resource with DateTime option
|
||||
win_dsc:
|
||||
resource_name: DateTimeResource
|
||||
DateTimeOption: '2019-02-22T13:57:31.2311892+00:00'
|
||||
|
||||
# more complex example using custom DSC resource and dict values
|
||||
- name: Setup the xWebAdministration module
|
||||
win_psmodule:
|
||||
name: xWebAdministration
|
||||
state: present
|
||||
|
||||
- name: Create IIS Website with Binding and Authentication options
|
||||
win_dsc:
|
||||
resource_name: xWebsite
|
||||
Ensure: Present
|
||||
Name: DSC Website
|
||||
State: Started
|
||||
PhysicalPath: C:\inetpub\wwwroot
|
||||
BindingInfo: # Example of a CimInstance[] DSC parameter (list of dicts)
|
||||
- Protocol: https
|
||||
Port: 1234
|
||||
CertificateStoreName: MY
|
||||
CertificateThumbprint: C676A89018C4D5902353545343634F35E6B3A659
|
||||
HostName: DSCTest
|
||||
IPAddress: '*'
|
||||
SSLFlags: '1'
|
||||
- Protocol: http
|
||||
Port: 4321
|
||||
IPAddress: '*'
|
||||
AuthenticationInfo: # Example of a CimInstance DSC parameter (dict)
|
||||
Anonymous: no
|
||||
Basic: true
|
||||
Digest: false
|
||||
Windows: yes
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
module_version:
|
||||
description: The version of the dsc resource/module used.
|
||||
returned: always
|
||||
type: str
|
||||
sample: "1.0.1"
|
||||
reboot_required:
|
||||
description: Flag returned from the DSC engine indicating whether or not
|
||||
the machine requires a reboot for the invoked changes to take effect.
|
||||
returned: always
|
||||
type: bool
|
||||
sample: true
|
||||
verbose_test:
|
||||
description: The verbose output as a list from executing the DSC test
|
||||
method.
|
||||
returned: Ansible verbosity is -vvv or greater
|
||||
type: list
|
||||
sample: [
|
||||
"Perform operation 'Invoke CimMethod' with the following parameters, ",
|
||||
"[SERVER]: LCM: [Start Test ] [[File]DirectResourceAccess]",
|
||||
"Operation 'Invoke CimMethod' complete."
|
||||
]
|
||||
verbose_set:
|
||||
description: The verbose output as a list from executing the DSC Set
|
||||
method.
|
||||
returned: Ansible verbosity is -vvv or greater and a change occurred
|
||||
type: list
|
||||
sample: [
|
||||
"Perform operation 'Invoke CimMethod' with the following parameters, ",
|
||||
"[SERVER]: LCM: [Start Set ] [[File]DirectResourceAccess]",
|
||||
"Operation 'Invoke CimMethod' complete."
|
||||
]
|
||||
'''
|
@ -1,111 +0,0 @@
|
||||
#!powershell
|
||||
|
||||
# Copyright: (c) 2014, Paul Durivage <paul.durivage@rackspace.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#Requires -Module Ansible.ModuleUtils.Legacy
|
||||
|
||||
Import-Module -Name ServerManager
|
||||
|
||||
$result = @{
|
||||
changed = $false
|
||||
}
|
||||
|
||||
$params = Parse-Args $args -supports_check_mode $true
|
||||
$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false
|
||||
|
||||
$name = Get-AnsibleParam -obj $params -name "name" -type "list" -failifempty $true
|
||||
$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent"
|
||||
|
||||
$include_sub_features = Get-AnsibleParam -obj $params -name "include_sub_features" -type "bool" -default $false
|
||||
$include_management_tools = Get-AnsibleParam -obj $params -name "include_management_tools" -type "bool" -default $false
|
||||
$source = Get-AnsibleParam -obj $params -name "source" -type "str"
|
||||
|
||||
$install_cmdlet = $false
|
||||
if (Get-Command -Name Install-WindowsFeature -ErrorAction SilentlyContinue) {
|
||||
Set-Alias -Name Install-AnsibleWindowsFeature -Value Install-WindowsFeature
|
||||
Set-Alias -Name Uninstall-AnsibleWindowsFeature -Value Uninstall-WindowsFeature
|
||||
$install_cmdlet = $true
|
||||
} elseif (Get-Command -Name Add-WindowsFeature -ErrorAction SilentlyContinue) {
|
||||
Set-Alias -Name Install-AnsibleWindowsFeature -Value Add-WindowsFeature
|
||||
Set-Alias -Name Uninstall-AnsibleWindowsFeature -Value Remove-WindowsFeature
|
||||
} else {
|
||||
Fail-Json -obj $result -message "This version of Windows does not support the cmdlets Install-WindowsFeature or Add-WindowsFeature"
|
||||
}
|
||||
|
||||
if ($state -eq "present") {
|
||||
$install_args = @{
|
||||
Name = $name
|
||||
IncludeAllSubFeature = $include_sub_features
|
||||
Restart = $false
|
||||
WhatIf = $check_mode
|
||||
ErrorAction = "Stop"
|
||||
}
|
||||
|
||||
if ($install_cmdlet) {
|
||||
$install_args.IncludeManagementTools = $include_management_tools
|
||||
$install_args.Confirm = $false
|
||||
if ($source) {
|
||||
if (-not (Test-Path -Path $source)) {
|
||||
Fail-Json -obj $result -message "Failed to find source path $source for feature install"
|
||||
}
|
||||
$install_args.Source = $source
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$action_results = Install-AnsibleWindowsFeature @install_args
|
||||
} catch {
|
||||
Fail-Json -obj $result -message "Failed to install Windows Feature: $($_.Exception.Message)"
|
||||
}
|
||||
} else {
|
||||
$uninstall_args = @{
|
||||
Name = $name
|
||||
Restart = $false
|
||||
WhatIf = $check_mode
|
||||
ErrorAction = "Stop"
|
||||
}
|
||||
if ($install_cmdlet) {
|
||||
$uninstall_args.IncludeManagementTools = $include_management_tools
|
||||
}
|
||||
|
||||
try {
|
||||
$action_results = Uninstall-AnsibleWindowsFeature @uninstall_args
|
||||
} catch {
|
||||
Fail-Json -obj $result -message "Failed to uninstall Windows Feature: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
# Loop through results and create a hash containing details about
|
||||
# each role/feature that is installed/removed
|
||||
# $action_results.FeatureResult is not empty if anything was changed
|
||||
$feature_results = @()
|
||||
foreach ($action_result in $action_results.FeatureResult) {
|
||||
$message = @()
|
||||
foreach ($msg in $action_result.Message) {
|
||||
$message += @{
|
||||
message_type = $msg.MessageType.ToString()
|
||||
error_code = $msg.ErrorCode
|
||||
text = $msg.Text
|
||||
}
|
||||
}
|
||||
|
||||
$feature_results += @{
|
||||
id = $action_result.Id
|
||||
display_name = $action_result.DisplayName
|
||||
message = $message
|
||||
reboot_required = ConvertTo-Bool -obj $action_result.RestartNeeded
|
||||
skip_reason = $action_result.SkipReason.ToString()
|
||||
success = ConvertTo-Bool -obj $action_result.Success
|
||||
restart_needed = ConvertTo-Bool -obj $action_result.RestartNeeded
|
||||
}
|
||||
$result.changed = $true
|
||||
}
|
||||
$result.feature_result = $feature_results
|
||||
$result.success = ConvertTo-Bool -obj $action_results.Success
|
||||
$result.exitcode = $action_results.ExitCode.ToString()
|
||||
$result.reboot_required = ConvertTo-Bool -obj $action_results.RestartNeeded
|
||||
# controls whether Ansible will fail or not
|
||||
$result.failed = (-not $action_results.Success)
|
||||
|
||||
Exit-Json -obj $result
|
@ -1,149 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2014, Paul Durivage <paul.durivage@rackspace.com>
|
||||
# Copyright: (c) 2014, Trond Hindenes <trond@hindenes.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# this is a windows documentation stub. actual code lives in the .ps1
|
||||
# file of the same name
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: win_feature
|
||||
version_added: "1.7"
|
||||
short_description: Installs and uninstalls Windows Features on Windows Server
|
||||
description:
|
||||
- Installs or uninstalls Windows Roles or Features on Windows Server.
|
||||
- This module uses the Add/Remove-WindowsFeature Cmdlets on Windows 2008 R2
|
||||
and Install/Uninstall-WindowsFeature Cmdlets on Windows 2012, which are not available on client os machines.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Names of roles or features to install as a single feature or a comma-separated list of features.
|
||||
- To list all available features use the PowerShell command C(Get-WindowsFeature).
|
||||
type: list
|
||||
required: yes
|
||||
state:
|
||||
description:
|
||||
- State of the features or roles on the system.
|
||||
type: str
|
||||
choices: [ absent, present ]
|
||||
default: present
|
||||
include_sub_features:
|
||||
description:
|
||||
- Adds all subfeatures of the specified feature.
|
||||
type: bool
|
||||
default: no
|
||||
include_management_tools:
|
||||
description:
|
||||
- Adds the corresponding management tools to the specified feature.
|
||||
- Not supported in Windows 2008 R2 and will be ignored.
|
||||
type: bool
|
||||
default: no
|
||||
source:
|
||||
description:
|
||||
- Specify a source to install the feature from.
|
||||
- Not supported in Windows 2008 R2 and will be ignored.
|
||||
- Can either be C({driveletter}:\sources\sxs) or C(\\{IP}\share\sources\sxs).
|
||||
type: str
|
||||
version_added: "2.1"
|
||||
seealso:
|
||||
- module: win_chocolatey
|
||||
- module: win_package
|
||||
author:
|
||||
- Paul Durivage (@angstwad)
|
||||
- Trond Hindenes (@trondhindenes)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Install IIS (Web-Server only)
|
||||
win_feature:
|
||||
name: Web-Server
|
||||
state: present
|
||||
|
||||
- name: Install IIS (Web-Server and Web-Common-Http)
|
||||
win_feature:
|
||||
name:
|
||||
- Web-Server
|
||||
- Web-Common-Http
|
||||
state: present
|
||||
|
||||
- name: Install NET-Framework-Core from file
|
||||
win_feature:
|
||||
name: NET-Framework-Core
|
||||
source: C:\Temp\iso\sources\sxs
|
||||
state: present
|
||||
|
||||
- name: Install IIS Web-Server with sub features and management tools
|
||||
win_feature:
|
||||
name: Web-Server
|
||||
state: present
|
||||
include_sub_features: yes
|
||||
include_management_tools: yes
|
||||
register: win_feature
|
||||
|
||||
- name: Reboot if installing Web-Server feature requires it
|
||||
win_reboot:
|
||||
when: win_feature.reboot_required
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
exitcode:
|
||||
description: The stringified exit code from the feature installation/removal command.
|
||||
returned: always
|
||||
type: str
|
||||
sample: Success
|
||||
feature_result:
|
||||
description: List of features that were installed or removed.
|
||||
returned: success
|
||||
type: complex
|
||||
sample:
|
||||
contains:
|
||||
display_name:
|
||||
description: Feature display name.
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Telnet Client"
|
||||
id:
|
||||
description: A list of KB article IDs that apply to the update.
|
||||
returned: always
|
||||
type: int
|
||||
sample: 44
|
||||
message:
|
||||
description: Any messages returned from the feature subsystem that occurred during installation or removal of this feature.
|
||||
returned: always
|
||||
type: list
|
||||
elements: str
|
||||
sample: []
|
||||
reboot_required:
|
||||
description: True when the target server requires a reboot as a result of installing or removing this feature.
|
||||
returned: always
|
||||
type: bool
|
||||
sample: true
|
||||
restart_needed:
|
||||
description: DEPRECATED in Ansible 2.4 (refer to C(reboot_required) instead). True when the target server requires a reboot as a
|
||||
result of installing or removing this feature.
|
||||
returned: always
|
||||
type: bool
|
||||
sample: true
|
||||
skip_reason:
|
||||
description: The reason a feature installation or removal was skipped.
|
||||
returned: always
|
||||
type: str
|
||||
sample: NotSkipped
|
||||
success:
|
||||
description: If the feature installation or removal was successful.
|
||||
returned: always
|
||||
type: bool
|
||||
sample: true
|
||||
reboot_required:
|
||||
description: True when the target server requires a reboot to complete updates (no further updates can be installed until after a reboot).
|
||||
returned: success
|
||||
type: bool
|
||||
sample: true
|
||||
'''
|
@ -1,416 +0,0 @@
|
||||
#!powershell
|
||||
|
||||
# Copyright: (c) 2016, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#AnsibleRequires -CSharpUtil Ansible.Basic
|
||||
#Requires -Module Ansible.ModuleUtils.LinkUtil
|
||||
|
||||
$spec = @{
|
||||
options = @{
|
||||
paths = @{ type = "list"; elements = "str"; required = $true }
|
||||
age = @{ type = "str" }
|
||||
age_stamp = @{ type = "str"; default = "mtime"; choices = "mtime", "ctime", "atime" }
|
||||
file_type = @{ type = "str"; default = "file"; choices = "file", "directory" }
|
||||
follow = @{ type = "bool"; default = $false }
|
||||
hidden = @{ type = "bool"; default = $false }
|
||||
patterns = @{ type = "list"; elements = "str"; aliases = "regex", "regexp" }
|
||||
recurse = @{ type = "bool"; default = $false }
|
||||
size = @{ type = "str" }
|
||||
use_regex = @{ type = "bool"; default = $false }
|
||||
get_checksum = @{ type = "bool"; default = $true }
|
||||
checksum_algorithm = @{ type = "str"; default = "sha1"; choices = "md5", "sha1", "sha256", "sha384", "sha512" }
|
||||
}
|
||||
supports_check_mode = $true
|
||||
}
|
||||
|
||||
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
|
||||
|
||||
$paths = $module.Params.paths
|
||||
$age = $module.Params.age
|
||||
$age_stamp = $module.Params.age_stamp
|
||||
$file_type = $module.Params.file_type
|
||||
$follow = $module.Params.follow
|
||||
$hidden = $module.Params.hidden
|
||||
$patterns = $module.Params.patterns
|
||||
$recurse = $module.Params.recurse
|
||||
$size = $module.Params.size
|
||||
$use_regex = $module.Params.use_regex
|
||||
$get_checksum = $module.Params.get_checksum
|
||||
$checksum_algorithm = $module.Params.checksum_algorithm
|
||||
|
||||
$module.Result.examined = 0
|
||||
$module.Result.files = @()
|
||||
$module.Result.matched = 0
|
||||
|
||||
Load-LinkUtils
|
||||
|
||||
Function Assert-Age {
|
||||
Param (
|
||||
[System.IO.FileSystemInfo]$File,
|
||||
[System.Int64]$Age,
|
||||
[System.String]$AgeStamp
|
||||
)
|
||||
|
||||
$actual_age = switch ($AgeStamp) {
|
||||
mtime { $File.LastWriteTime.Ticks }
|
||||
ctime { $File.CreationTime.Ticks }
|
||||
atime { $File.LastAccessTime.Ticks }
|
||||
}
|
||||
|
||||
if ($Age -ge 0) {
|
||||
return $Age -ge $actual_age
|
||||
} else {
|
||||
return ($Age * -1) -le $actual_age
|
||||
}
|
||||
}
|
||||
|
||||
Function Assert-FileType {
|
||||
Param (
|
||||
[System.IO.FileSystemInfo]$File,
|
||||
[System.String]$FileType
|
||||
)
|
||||
|
||||
$is_dir = $File.Attributes.HasFlag([System.IO.FileAttributes]::Directory)
|
||||
return ($FileType -eq 'directory' -and $is_dir) -or ($FileType -eq 'file' -and -not $is_dir)
|
||||
}
|
||||
|
||||
Function Assert-FileHidden {
|
||||
Param (
|
||||
[System.IO.FileSystemInfo]$File,
|
||||
[Switch]$IsHidden
|
||||
)
|
||||
|
||||
$file_is_hidden = $File.Attributes.HasFlag([System.IO.FileAttributes]::Hidden)
|
||||
return $IsHidden.IsPresent -eq $file_is_hidden
|
||||
}
|
||||
|
||||
|
||||
Function Assert-FileNamePattern {
|
||||
Param (
|
||||
[System.IO.FileSystemInfo]$File,
|
||||
[System.String[]]$Patterns,
|
||||
[Switch]$UseRegex
|
||||
)
|
||||
|
||||
$valid_match = $false
|
||||
foreach ($pattern in $Patterns) {
|
||||
if ($UseRegex) {
|
||||
if ($File.Name -match $pattern) {
|
||||
$valid_match = $true
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if ($File.Name -like $pattern) {
|
||||
$valid_match = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return $valid_match
|
||||
}
|
||||
|
||||
Function Assert-FileSize {
|
||||
Param (
|
||||
[System.IO.FileSystemInfo]$File,
|
||||
[System.Int64]$Size
|
||||
)
|
||||
|
||||
if ($Size -ge 0) {
|
||||
return $File.Length -ge $Size
|
||||
} else {
|
||||
return $File.Length -le ($Size * -1)
|
||||
}
|
||||
}
|
||||
|
||||
Function Get-FileChecksum {
|
||||
Param (
|
||||
[System.String]$Path,
|
||||
[System.String]$Algorithm
|
||||
)
|
||||
|
||||
$sp = switch ($algorithm) {
|
||||
'md5' { New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider }
|
||||
'sha1' { New-Object -TypeName System.Security.Cryptography.SHA1CryptoServiceProvider }
|
||||
'sha256' { New-Object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider }
|
||||
'sha384' { New-Object -TypeName System.Security.Cryptography.SHA384CryptoServiceProvider }
|
||||
'sha512' { New-Object -TypeName System.Security.Cryptography.SHA512CryptoServiceProvider }
|
||||
}
|
||||
|
||||
$fp = [System.IO.File]::Open($Path, [System.IO.Filemode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
|
||||
try {
|
||||
$hash = [System.BitConverter]::ToString($sp.ComputeHash($fp)).Replace("-", "").ToLower()
|
||||
} finally {
|
||||
$fp.Dispose()
|
||||
}
|
||||
|
||||
return $hash
|
||||
}
|
||||
|
||||
Function Search-Path {
|
||||
[CmdletBinding()]
|
||||
Param (
|
||||
[Parameter(Mandatory=$true)]
|
||||
[System.String]
|
||||
$Path,
|
||||
|
||||
[Parameter(Mandatory=$true)]
|
||||
[AllowEmptyCollection()]
|
||||
[System.Collections.Generic.HashSet`1[System.String]]
|
||||
$CheckedPaths,
|
||||
|
||||
[Parameter(Mandatory=$true)]
|
||||
[Object]
|
||||
$Module,
|
||||
|
||||
[System.Int64]
|
||||
$Age,
|
||||
|
||||
[System.String]
|
||||
$AgeStamp,
|
||||
|
||||
[System.String]
|
||||
$FileType,
|
||||
|
||||
[Switch]
|
||||
$Follow,
|
||||
|
||||
[Switch]
|
||||
$GetChecksum,
|
||||
|
||||
[Switch]
|
||||
$IsHidden,
|
||||
|
||||
[System.String[]]
|
||||
$Patterns,
|
||||
|
||||
[Switch]
|
||||
$Recurse,
|
||||
|
||||
[System.Int64]
|
||||
$Size,
|
||||
|
||||
[Switch]
|
||||
$UseRegex
|
||||
)
|
||||
|
||||
$dir_obj = New-Object -TypeName System.IO.DirectoryInfo -ArgumentList $Path
|
||||
if ([Int32]$dir_obj.Attributes -eq -1) {
|
||||
$Module.Warn("Argument path '$Path' does not exist, skipping")
|
||||
return
|
||||
} elseif (-not $dir_obj.Attributes.HasFlag([System.IO.FileAttributes]::Directory)) {
|
||||
$Module.Warn("Argument path '$Path' is a file not a directory, skipping")
|
||||
return
|
||||
}
|
||||
|
||||
$dir_files = @()
|
||||
try {
|
||||
$dir_files = $dir_obj.EnumerateFileSystemInfos("*", [System.IO.SearchOption]::TopDirectoryOnly)
|
||||
} catch [System.IO.DirectoryNotFoundException] { # Broken ReparsePoint/Symlink, cannot enumerate
|
||||
} catch [System.UnauthorizedAccessException] {} # No ListDirectory permissions, Get-ChildItem ignored this
|
||||
|
||||
foreach ($dir_child in $dir_files) {
|
||||
if ($dir_child.Attributes.HasFlag([System.IO.FileAttributes]::Directory) -and $Recurse) {
|
||||
if ($Follow -or -not $dir_child.Attributes.HasFlag([System.IO.FileAttributes]::ReparsePoint)) {
|
||||
$PSBoundParameters.Remove('Path') > $null
|
||||
Search-Path -Path $dir_child.FullName @PSBoundParameters
|
||||
}
|
||||
}
|
||||
|
||||
# Check to see if we've already encountered this path and skip if we have.
|
||||
if (-not $CheckedPaths.Add($dir_child.FullName.ToLowerInvariant())) {
|
||||
continue
|
||||
}
|
||||
|
||||
$Module.Result.examined++
|
||||
|
||||
if ($PSBoundParameters.ContainsKey('Age')) {
|
||||
$age_match = Assert-Age -File $dir_child -Age $Age -AgeStamp $AgeStamp
|
||||
} else {
|
||||
$age_match = $true
|
||||
}
|
||||
|
||||
$file_type_match = Assert-FileType -File $dir_child -FileType $FileType
|
||||
$hidden_match = Assert-FileHidden -File $dir_child -IsHidden:$IsHidden
|
||||
|
||||
if ($PSBoundParameters.ContainsKey('Patterns')) {
|
||||
$pattern_match = Assert-FileNamePattern -File $dir_child -Patterns $Patterns -UseRegex:$UseRegex.IsPresent
|
||||
} else {
|
||||
$pattern_match = $true
|
||||
}
|
||||
|
||||
if ($PSBoundParameters.ContainsKey('Size')) {
|
||||
$size_match = Assert-FileSize -File $dir_child -Size $Size
|
||||
} else {
|
||||
$size_match = $true
|
||||
}
|
||||
|
||||
if (-not ($age_match -and $file_type_match -and $hidden_match -and $pattern_match -and $size_match)) {
|
||||
continue
|
||||
}
|
||||
|
||||
# It passed all our filters so add it
|
||||
$module.Result.matched++
|
||||
|
||||
# TODO: Make this generic so it can be shared with win_find and win_stat.
|
||||
$epoch = New-Object -Type System.DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0
|
||||
$file_info = @{
|
||||
attributes = $dir_child.Attributes.ToString()
|
||||
checksum = $null
|
||||
creationtime = (New-TimeSpan -Start $epoch -End $dir_child.CreationTime).TotalSeconds
|
||||
exists = $true
|
||||
extension = $null
|
||||
filename = $dir_child.Name
|
||||
isarchive = $dir_child.Attributes.HasFlag([System.IO.FileAttributes]::Archive)
|
||||
isdir = $dir_child.Attributes.HasFlag([System.IO.FileAttributes]::Directory)
|
||||
ishidden = $dir_child.Attributes.HasFlag([System.IO.FileAttributes]::Hidden)
|
||||
isreadonly = $dir_child.Attributes.HasFlag([System.IO.FileAttributes]::ReadOnly)
|
||||
isreg = $false
|
||||
isshared = $false
|
||||
lastaccesstime = (New-TimeSpan -Start $epoch -End $dir_child.LastAccessTime).TotalSeconds
|
||||
lastwritetime = (New-TimeSpan -Start $epoch -End $dir_child.LastWriteTime).TotalSeconds
|
||||
owner = $null
|
||||
path = $dir_child.FullName
|
||||
sharename = $null
|
||||
size = $null
|
||||
}
|
||||
|
||||
try {
|
||||
$file_info.owner = $dir_child.GetAccessControl().Owner
|
||||
} catch {} # May not have rights to get the Owner, historical behaviour is to ignore.
|
||||
|
||||
if ($dir_child.Attributes.HasFlag([System.IO.FileAttributes]::Directory)) {
|
||||
$share_info = Get-CimInstance -ClassName Win32_Share -Filter "Path='$($dir_child.FullName -replace '\\', '\\')'"
|
||||
if ($null -ne $share_info) {
|
||||
$file_info.isshared = $true
|
||||
$file_info.sharename = $share_info.Name
|
||||
}
|
||||
} else {
|
||||
$file_info.extension = $dir_child.Extension
|
||||
$file_info.isreg = $true
|
||||
$file_info.size = $dir_child.Length
|
||||
|
||||
if ($GetChecksum) {
|
||||
try {
|
||||
$file_info.checksum = Get-FileChecksum -Path $dir_child.FullName -Algorithm $checksum_algorithm
|
||||
} catch {} # Just keep the checksum as $null in the case of a failure.
|
||||
}
|
||||
}
|
||||
|
||||
# Append the link information if the path is a link
|
||||
$link_info = @{
|
||||
isjunction = $false
|
||||
islnk = $false
|
||||
nlink = 1
|
||||
lnk_source = $null
|
||||
lnk_target = $null
|
||||
hlnk_targets = @()
|
||||
}
|
||||
$link_stat = Get-Link -link_path $dir_child.FullName
|
||||
if ($null -ne $link_stat) {
|
||||
switch ($link_stat.Type) {
|
||||
"SymbolicLink" {
|
||||
$link_info.islnk = $true
|
||||
$link_info.isreg = $false
|
||||
$link_info.lnk_source = $link_stat.AbsolutePath
|
||||
$link_info.lnk_target = $link_stat.TargetPath
|
||||
break
|
||||
}
|
||||
"JunctionPoint" {
|
||||
$link_info.isjunction = $true
|
||||
$link_info.isreg = $false
|
||||
$link_info.lnk_source = $link_stat.AbsolutePath
|
||||
$link_info.lnk_target = $link_stat.TargetPath
|
||||
break
|
||||
}
|
||||
"HardLink" {
|
||||
$link_info.nlink = $link_stat.HardTargets.Count
|
||||
|
||||
# remove current path from the targets
|
||||
$hlnk_targets = $link_info.HardTargets | Where-Object { $_ -ne $dir_child.FullName }
|
||||
$link_info.hlnk_targets = @($hlnk_targets)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($kv in $link_info.GetEnumerator()) {
|
||||
$file_info.$($kv.Key) = $kv.Value
|
||||
}
|
||||
|
||||
# Output the file_info object
|
||||
$file_info
|
||||
}
|
||||
}
|
||||
|
||||
$search_params = @{
|
||||
CheckedPaths = [System.Collections.Generic.HashSet`1[System.String]]@()
|
||||
GetChecksum = $get_checksum
|
||||
Module = $module
|
||||
FileType = $file_type
|
||||
Follow = $follow
|
||||
IsHidden = $hidden
|
||||
Recurse = $recurse
|
||||
}
|
||||
|
||||
if ($null -ne $age) {
|
||||
$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) {
|
||||
$specified_seconds = [Int64]$Matches[1]
|
||||
if ($null -eq $Matches[2]) {
|
||||
$chosen_unit = 's'
|
||||
} else {
|
||||
$chosen_unit = $Matches[2]
|
||||
}
|
||||
|
||||
$total_seconds = $specified_seconds * ($seconds_per_unit.$chosen_unit)
|
||||
|
||||
if ($total_seconds -ge 0) {
|
||||
$search_params.Age = (Get-Date).AddSeconds($total_seconds * -1).Ticks
|
||||
} else {
|
||||
# Make sure we add the positive value of seconds to current time then make it negative for later comparisons.
|
||||
$age = (Get-Date).AddSeconds($total_seconds).Ticks
|
||||
$search_params.Age = $age * -1
|
||||
}
|
||||
$search_params.AgeStamp = $age_stamp
|
||||
} else {
|
||||
$module.FailJson("Invalid age pattern specified")
|
||||
}
|
||||
}
|
||||
|
||||
if ($null -ne $patterns) {
|
||||
$search_params.Patterns = $patterns
|
||||
$search_params.UseRegex = $use_regex
|
||||
}
|
||||
|
||||
if ($null -ne $size) {
|
||||
$bytes_per_unit = @{'b'=1; 'k'=1KB; 'm'=1MB; 'g'=1GB;'t'=1TB}
|
||||
$size_pattern = '^(-?\d+)(b|k|m|g|t)?$'
|
||||
$match = $size -match $size_pattern
|
||||
if ($Match) {
|
||||
$specified_size = [Int64]$Matches[1]
|
||||
if ($null -eq $Matches[2]) {
|
||||
$chosen_byte = 'b'
|
||||
} else {
|
||||
$chosen_byte = $Matches[2]
|
||||
}
|
||||
|
||||
$search_params.Size = $specified_size * ($bytes_per_unit.$chosen_byte)
|
||||
} else {
|
||||
$module.FailJson("Invalid size pattern specified")
|
||||
}
|
||||
}
|
||||
|
||||
$matched_files = foreach ($path in $paths) {
|
||||
# Ensure we pass in an absolute path. We use the ExecutionContext as this is based on the PSProvider path not the
|
||||
# process location which can be different.
|
||||
$abs_path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path)
|
||||
Search-Path -Path $abs_path @search_params
|
||||
}
|
||||
|
||||
# Make sure we sort the files in alphabetical order.
|
||||
$module.Result.files = @() + ($matched_files | Sort-Object -Property {$_.path})
|
||||
|
||||
$module.ExitJson()
|
||||
|
@ -1,345 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2016, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# this is a windows documentation stub. actual code lives in the .ps1
|
||||
# file of the same name
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
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.
|
||||
- For non-Windows targets, use the M(find) module instead.
|
||||
options:
|
||||
age:
|
||||
description:
|
||||
- Select files or folders whose age is equal to or greater than
|
||||
the specified time.
|
||||
- Use a negative age to find files equal to or less than
|
||||
the specified time.
|
||||
- You can choose seconds, minutes, hours, days or weeks
|
||||
by specifying the first letter of an of
|
||||
those words (e.g., "2s", "10d", 1w").
|
||||
type: str
|
||||
age_stamp:
|
||||
description:
|
||||
- Choose the file property against which we compare C(age).
|
||||
- The default attribute we compare with is the last modification time.
|
||||
type: str
|
||||
choices: [ atime, ctime, mtime ]
|
||||
default: mtime
|
||||
checksum_algorithm:
|
||||
description:
|
||||
- Algorithm to determine the checksum of a file.
|
||||
- Will throw an error if the host is unable to use specified algorithm.
|
||||
type: str
|
||||
choices: [ md5, sha1, sha256, sha384, sha512 ]
|
||||
default: sha1
|
||||
file_type:
|
||||
description: Type of file to search for.
|
||||
type: str
|
||||
choices: [ directory, file ]
|
||||
default: file
|
||||
follow:
|
||||
description:
|
||||
- Set this to C(yes) to follow symlinks in the path.
|
||||
- This needs to be used in conjunction with C(recurse).
|
||||
type: bool
|
||||
default: no
|
||||
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.
|
||||
type: bool
|
||||
default: yes
|
||||
hidden:
|
||||
description: Set this to include hidden files or folders.
|
||||
type: bool
|
||||
default: no
|
||||
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.
|
||||
type: list
|
||||
required: yes
|
||||
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 restrict 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.
|
||||
type: list
|
||||
aliases: [ "regex", "regexp" ]
|
||||
recurse:
|
||||
description:
|
||||
- Will recursively descend into the directory looking for files or folders.
|
||||
type: bool
|
||||
default: no
|
||||
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.
|
||||
type: str
|
||||
use_regex:
|
||||
description:
|
||||
- Will set patterns to run as a regex check if set to C(yes).
|
||||
type: bool
|
||||
default: no
|
||||
author:
|
||||
- Jordan Borean (@jborean93)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Find files in path
|
||||
win_find:
|
||||
paths: D:\Temp
|
||||
|
||||
- name: Find hidden files in path
|
||||
win_find:
|
||||
paths: D:\Temp
|
||||
hidden: yes
|
||||
|
||||
- name: Find files in multiple paths
|
||||
win_find:
|
||||
paths:
|
||||
- C:\Temp
|
||||
- D:\Temp
|
||||
|
||||
- name: Find files in directory while searching recursively
|
||||
win_find:
|
||||
paths: D:\Temp
|
||||
recurse: yes
|
||||
|
||||
- name: Find files in directory while following symlinks
|
||||
win_find:
|
||||
paths: D:\Temp
|
||||
recurse: yes
|
||||
follow: yes
|
||||
|
||||
- name: Find files with .log and .out extension using powershell wildcards
|
||||
win_find:
|
||||
paths: D:\Temp
|
||||
patterns: [ '*.log', '*.out' ]
|
||||
|
||||
- name: Find files in path based on regex pattern
|
||||
win_find:
|
||||
paths: D:\Temp
|
||||
patterns: out_\d{8}-\d{6}.log
|
||||
|
||||
- name: Find files older than 1 day
|
||||
win_find:
|
||||
paths: D:\Temp
|
||||
age: 86400
|
||||
|
||||
- name: Find files older than 1 day based on create time
|
||||
win_find:
|
||||
paths: D:\Temp
|
||||
age: 86400
|
||||
age_stamp: ctime
|
||||
|
||||
- name: Find files older than 1 day with unit syntax
|
||||
win_find:
|
||||
paths: D:\Temp
|
||||
age: 1d
|
||||
|
||||
- name: Find files newer than 1 hour
|
||||
win_find:
|
||||
paths: D:\Temp
|
||||
age: -3600
|
||||
|
||||
- name: Find files newer than 1 hour with unit syntax
|
||||
win_find:
|
||||
paths: D:\Temp
|
||||
age: -1h
|
||||
|
||||
- name: Find files larger than 1MB
|
||||
win_find:
|
||||
paths: D:\Temp
|
||||
size: 1048576
|
||||
|
||||
- name: Find files larger than 1GB with unit syntax
|
||||
win_find:
|
||||
paths: D:\Temp
|
||||
size: 1g
|
||||
|
||||
- name: Find files smaller than 1MB
|
||||
win_find:
|
||||
paths: D:\Temp
|
||||
size: -1048576
|
||||
|
||||
- name: Find files smaller than 1GB with unit syntax
|
||||
win_find:
|
||||
paths: D:\Temp
|
||||
size: -1g
|
||||
|
||||
- name: Find folders/symlinks in multiple paths
|
||||
win_find:
|
||||
paths:
|
||||
- C:\Temp
|
||||
- D:\Temp
|
||||
file_type: directory
|
||||
|
||||
- name: Find files and return SHA256 checksum of files found
|
||||
win_find:
|
||||
paths: C:\Temp
|
||||
get_checksum: yes
|
||||
checksum_algorithm: sha256
|
||||
|
||||
- name: Find files and do not return the checksum
|
||||
win_find:
|
||||
paths: C:\Temp
|
||||
get_checksum: no
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
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.
|
||||
returned: 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. The entries are sorted by the path value alphabetically.
|
||||
returned: success
|
||||
type: complex
|
||||
contains:
|
||||
attributes:
|
||||
description: attributes of the file at path in raw form.
|
||||
returned: success, path exists
|
||||
type: str
|
||||
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: str
|
||||
sample: 09cb79e8fc7453c84a07f644e441fd81623b7f98
|
||||
creationtime:
|
||||
description: The create time of the file represented in seconds since epoch.
|
||||
returned: success, path exists
|
||||
type: float
|
||||
sample: 1477984205.15
|
||||
exists:
|
||||
description: Whether the file exists, will always be true for M(win_find).
|
||||
returned: success, path exists
|
||||
type: bool
|
||||
sample: true
|
||||
extension:
|
||||
description: The extension of the file at path.
|
||||
returned: success, path exists, path is a file
|
||||
type: str
|
||||
sample: ".ps1"
|
||||
filename:
|
||||
description: The name of the file.
|
||||
returned: success, path exists
|
||||
type: str
|
||||
sample: temp
|
||||
hlnk_targets:
|
||||
description: List of other files pointing to the same file (hard links), excludes the current file.
|
||||
returned: success, path exists
|
||||
type: list
|
||||
sample:
|
||||
- C:\temp\file.txt
|
||||
- C:\Windows\update.log
|
||||
isarchive:
|
||||
description: If the path is ready for archiving or not.
|
||||
returned: success, path exists
|
||||
type: bool
|
||||
sample: true
|
||||
isdir:
|
||||
description: If the path is a directory or not.
|
||||
returned: success, path exists
|
||||
type: bool
|
||||
sample: true
|
||||
ishidden:
|
||||
description: If the path is hidden or not.
|
||||
returned: success, path exists
|
||||
type: bool
|
||||
sample: true
|
||||
isjunction:
|
||||
description: If the path is a junction point.
|
||||
returned: success, path exists
|
||||
type: bool
|
||||
sample: true
|
||||
islnk:
|
||||
description: If the path is a symbolic link.
|
||||
returned: success, path exists
|
||||
type: bool
|
||||
sample: true
|
||||
isreadonly:
|
||||
description: If the path is read only or not.
|
||||
returned: success, path exists
|
||||
type: bool
|
||||
sample: true
|
||||
isreg:
|
||||
description: If the path is a regular file or not.
|
||||
returned: success, path exists
|
||||
type: bool
|
||||
sample: true
|
||||
isshared:
|
||||
description: If the path is shared or not.
|
||||
returned: success, path exists
|
||||
type: bool
|
||||
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 symlink normalized for the remote filesystem.
|
||||
returned: success, path exists, path is a symbolic link or junction point
|
||||
type: str
|
||||
sample: C:\temp
|
||||
lnk_target:
|
||||
description: The target of the symlink. Note that relative paths remain relative, will return null if not a link.
|
||||
returned: success, path exists, path is a symbolic link or junction point
|
||||
type: str
|
||||
sample: temp
|
||||
nlink:
|
||||
description: Number of links to the file (hard links)
|
||||
returned: success, path exists
|
||||
type: int
|
||||
sample: 1
|
||||
owner:
|
||||
description: The owner of the file.
|
||||
returned: success, path exists
|
||||
type: str
|
||||
sample: BUILTIN\Administrators
|
||||
path:
|
||||
description: The full absolute path to the file.
|
||||
returned: success, path exists
|
||||
type: str
|
||||
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: str
|
||||
sample: file-share
|
||||
size:
|
||||
description: The size in bytes of the file.
|
||||
returned: success, path exists, path is a file
|
||||
type: int
|
||||
sample: 1024
|
||||
'''
|
@ -1,200 +0,0 @@
|
||||
#!powershell
|
||||
|
||||
# Copyright: (c) 2019, Varun Chopra (@chopraaa) <v@chopraaa.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#AnsibleRequires -CSharpUtil Ansible.Basic
|
||||
#AnsibleRequires -OSVersion 6.2
|
||||
|
||||
Set-StrictMode -Version 2
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$spec = @{
|
||||
options = @{
|
||||
drive_letter = @{ type = "str" }
|
||||
path = @{ type = "str" }
|
||||
label = @{ type = "str" }
|
||||
new_label = @{ type = "str" }
|
||||
file_system = @{ type = "str"; choices = "ntfs", "refs", "exfat", "fat32", "fat" }
|
||||
allocation_unit_size = @{ type = "int" }
|
||||
large_frs = @{ type = "bool" }
|
||||
full = @{ type = "bool"; default = $false }
|
||||
compress = @{ type = "bool" }
|
||||
integrity_streams = @{ type = "bool" }
|
||||
force = @{ type = "bool"; default = $false }
|
||||
}
|
||||
mutually_exclusive = @(
|
||||
,@('drive_letter', 'path', 'label')
|
||||
)
|
||||
required_one_of = @(
|
||||
,@('drive_letter', 'path', 'label')
|
||||
)
|
||||
supports_check_mode = $true
|
||||
}
|
||||
|
||||
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
|
||||
|
||||
$drive_letter = $module.Params.drive_letter
|
||||
$path = $module.Params.path
|
||||
$label = $module.Params.label
|
||||
$new_label = $module.Params.new_label
|
||||
$file_system = $module.Params.file_system
|
||||
$allocation_unit_size = $module.Params.allocation_unit_size
|
||||
$large_frs = $module.Params.large_frs
|
||||
$full_format = $module.Params.full
|
||||
$compress_volume = $module.Params.compress
|
||||
$integrity_streams = $module.Params.integrity_streams
|
||||
$force_format = $module.Params.force
|
||||
|
||||
# Some pre-checks
|
||||
if ($null -ne $drive_letter -and $drive_letter -notmatch "^[a-zA-Z]$") {
|
||||
$module.FailJson("The parameter drive_letter should be a single character A-Z")
|
||||
}
|
||||
if ($integrity_streams -eq $true -and $file_system -ne "refs") {
|
||||
$module.FailJson("Integrity streams can be enabled only on ReFS volumes. You specified: $($file_system)")
|
||||
}
|
||||
if ($compress_volume -eq $true) {
|
||||
if ($file_system -eq "ntfs") {
|
||||
if ($null -ne $allocation_unit_size -and $allocation_unit_size -gt 4096) {
|
||||
$module.FailJson("NTFS compression is not supported for allocation unit sizes above 4096")
|
||||
}
|
||||
}
|
||||
else {
|
||||
$module.FailJson("Compression can be enabled only on NTFS volumes. You specified: $($file_system)")
|
||||
}
|
||||
}
|
||||
|
||||
function Get-AnsibleVolume {
|
||||
param(
|
||||
$DriveLetter,
|
||||
$Path,
|
||||
$Label
|
||||
)
|
||||
|
||||
if ($null -ne $DriveLetter) {
|
||||
try {
|
||||
$volume = Get-Volume -DriveLetter $DriveLetter
|
||||
} catch {
|
||||
$module.FailJson("There was an error retrieving the volume using drive_letter $($DriveLetter): $($_.Exception.Message)", $_)
|
||||
}
|
||||
}
|
||||
elseif ($null -ne $Path) {
|
||||
try {
|
||||
$volume = Get-Volume -Path $Path
|
||||
} catch {
|
||||
$module.FailJson("There was an error retrieving the volume using path $($Path): $($_.Exception.Message)", $_)
|
||||
}
|
||||
}
|
||||
elseif ($null -ne $Label) {
|
||||
try {
|
||||
$volume = Get-Volume -FileSystemLabel $Label
|
||||
} catch {
|
||||
$module.FailJson("There was an error retrieving the volume using label $($Label): $($_.Exception.Message)", $_)
|
||||
}
|
||||
}
|
||||
else {
|
||||
$module.FailJson("Unable to locate volume: drive_letter, path and label were not specified")
|
||||
}
|
||||
|
||||
return $volume
|
||||
}
|
||||
|
||||
function Format-AnsibleVolume {
|
||||
param(
|
||||
$Path,
|
||||
$Label,
|
||||
$FileSystem,
|
||||
$Full,
|
||||
$UseLargeFRS,
|
||||
$Compress,
|
||||
$SetIntegrityStreams,
|
||||
$AllocationUnitSize
|
||||
)
|
||||
$parameters = @{
|
||||
Path = $Path
|
||||
Full = $Full
|
||||
}
|
||||
if ($null -ne $UseLargeFRS) {
|
||||
$parameters.Add("UseLargeFRS", $UseLargeFRS)
|
||||
}
|
||||
if ($null -ne $SetIntegrityStreams) {
|
||||
$parameters.Add("SetIntegrityStreams", $SetIntegrityStreams)
|
||||
}
|
||||
if ($null -ne $Compress){
|
||||
$parameters.Add("Compress", $Compress)
|
||||
}
|
||||
if ($null -ne $Label) {
|
||||
$parameters.Add("NewFileSystemLabel", $Label)
|
||||
}
|
||||
if ($null -ne $FileSystem) {
|
||||
$parameters.Add("FileSystem", $FileSystem)
|
||||
}
|
||||
if ($null -ne $AllocationUnitSize) {
|
||||
$parameters.Add("AllocationUnitSize", $AllocationUnitSize)
|
||||
}
|
||||
|
||||
Format-Volume @parameters -Confirm:$false | Out-Null
|
||||
|
||||
}
|
||||
|
||||
$ansible_volume = Get-AnsibleVolume -DriveLetter $drive_letter -Path $path -Label $label
|
||||
$ansible_file_system = $ansible_volume.FileSystem
|
||||
$ansible_volume_size = $ansible_volume.Size
|
||||
$ansible_volume_alu = (Get-CimInstance -ClassName Win32_Volume -Filter "DeviceId = '$($ansible_volume.path.replace('\','\\'))'" -Property BlockSize).BlockSize
|
||||
|
||||
$ansible_partition = Get-Partition -Volume $ansible_volume
|
||||
|
||||
if (-not $force_format -and $null -ne $allocation_unit_size -and $ansible_volume_alu -ne 0 -and $null -ne $ansible_volume_alu -and $allocation_unit_size -ne $ansible_volume_alu) {
|
||||
$module.FailJson("Force format must be specified since target allocation unit size: $($allocation_unit_size) is different from the current allocation unit size of the volume: $($ansible_volume_alu)")
|
||||
}
|
||||
|
||||
foreach ($access_path in $ansible_partition.AccessPaths) {
|
||||
if ($access_path -ne $Path) {
|
||||
if ($null -ne $file_system -and
|
||||
-not [string]::IsNullOrEmpty($ansible_file_system) -and
|
||||
$file_system -ne $ansible_file_system)
|
||||
{
|
||||
if (-not $force_format)
|
||||
{
|
||||
$no_files_in_volume = (Get-ChildItem -LiteralPath $access_path -ErrorAction SilentlyContinue | Measure-Object).Count -eq 0
|
||||
if($no_files_in_volume)
|
||||
{
|
||||
$module.FailJson("Force format must be specified since target file system: $($file_system) is different from the current file system of the volume: $($ansible_file_system.ToLower())")
|
||||
}
|
||||
else
|
||||
{
|
||||
$module.FailJson("Force format must be specified to format non-pristine volumes")
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$pristine = -not $force_format
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($force_format) {
|
||||
if (-not $module.CheckMode) {
|
||||
Format-AnsibleVolume -Path $ansible_volume.Path -Full $full_format -Label $new_label -FileSystem $file_system -SetIntegrityStreams $integrity_streams -UseLargeFRS $large_frs -Compress $compress_volume -AllocationUnitSize $allocation_unit_size
|
||||
}
|
||||
$module.Result.changed = $true
|
||||
}
|
||||
else {
|
||||
if ($pristine) {
|
||||
if ($null -eq $new_label) {
|
||||
$new_label = $ansible_volume.FileSystemLabel
|
||||
}
|
||||
# Conditions for formatting
|
||||
if ($ansible_volume_size -eq 0 -or
|
||||
$ansible_volume.FileSystemLabel -ne $new_label) {
|
||||
if (-not $module.CheckMode) {
|
||||
Format-AnsibleVolume -Path $ansible_volume.Path -Full $full_format -Label $new_label -FileSystem $file_system -SetIntegrityStreams $integrity_streams -UseLargeFRS $large_frs -Compress $compress_volume -AllocationUnitSize $allocation_unit_size
|
||||
}
|
||||
$module.Result.changed = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$module.ExitJson()
|
@ -1,103 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2019, Varun Chopra (@chopraaa) <v@chopraaa.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'
|
||||
}
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
module: win_format
|
||||
version_added: '2.8'
|
||||
short_description: Formats an existing volume or a new volume on an existing partition on Windows
|
||||
description:
|
||||
- The M(win_format) module formats an existing volume or a new volume on an existing partition on Windows
|
||||
options:
|
||||
drive_letter:
|
||||
description:
|
||||
- Used to specify the drive letter of the volume to be formatted.
|
||||
type: str
|
||||
path:
|
||||
description:
|
||||
- Used to specify the path to the volume to be formatted.
|
||||
type: str
|
||||
label:
|
||||
description:
|
||||
- Used to specify the label of the volume to be formatted.
|
||||
type: str
|
||||
new_label:
|
||||
description:
|
||||
- Used to specify the new file system label of the formatted volume.
|
||||
type: str
|
||||
file_system:
|
||||
description:
|
||||
- Used to specify the file system to be used when formatting the target volume.
|
||||
type: str
|
||||
choices: [ ntfs, refs, exfat, fat32, fat ]
|
||||
allocation_unit_size:
|
||||
description:
|
||||
- Specifies the cluster size to use when formatting the volume.
|
||||
- If no cluster size is specified when you format a partition, defaults are selected based on
|
||||
the size of the partition.
|
||||
- This value must be a multiple of the physical sector size of the disk.
|
||||
type: int
|
||||
large_frs:
|
||||
description:
|
||||
- Specifies that large File Record System (FRS) should be used.
|
||||
type: bool
|
||||
compress:
|
||||
description:
|
||||
- Enable compression on the resulting NTFS volume.
|
||||
- NTFS compression is not supported where I(allocation_unit_size) is more than 4096.
|
||||
type: bool
|
||||
integrity_streams:
|
||||
description:
|
||||
- Enable integrity streams on the resulting ReFS volume.
|
||||
type: bool
|
||||
full:
|
||||
description:
|
||||
- A full format writes to every sector of the disk, takes much longer to perform than the
|
||||
default (quick) format, and is not recommended on storage that is thinly provisioned.
|
||||
- Specify C(true) for full format.
|
||||
type: bool
|
||||
force:
|
||||
description:
|
||||
- Specify if formatting should be forced for volumes that are not created from new partitions
|
||||
or if the source and target file system are different.
|
||||
type: bool
|
||||
notes:
|
||||
- Microsoft Windows Server 2012 or Microsoft Windows 8 or newer is required to use this module. To check if your system is compatible, see
|
||||
U(https://docs.microsoft.com/en-us/windows/desktop/sysinfo/operating-system-version).
|
||||
- One of three parameters (I(drive_letter), I(path) and I(label)) are mandatory to identify the target
|
||||
volume but more than one cannot be specified at the same time.
|
||||
- This module is idempotent if I(force) is not specified and file system labels remain preserved.
|
||||
- For more information, see U(https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/format-msft-volume)
|
||||
seealso:
|
||||
- module: win_disk_facts
|
||||
- module: win_partition
|
||||
author:
|
||||
- Varun Chopra (@chopraaa) <v@chopraaa.com>
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Create a partition with drive letter D and size 5 GiB
|
||||
win_partition:
|
||||
drive_letter: D
|
||||
partition_size: 5 GiB
|
||||
disk_number: 1
|
||||
|
||||
- name: Full format the newly created partition as NTFS and label it
|
||||
win_format:
|
||||
drive_letter: D
|
||||
file_system: NTFS
|
||||
new_label: Formatted
|
||||
full: True
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
#
|
||||
'''
|
@ -1,145 +0,0 @@
|
||||
#!powershell
|
||||
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#Requires -Module Ansible.ModuleUtils.Legacy
|
||||
|
||||
Set-StrictMode -Version 2
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$system_path = "System\CurrentControlSet\Control\Session Manager\Environment"
|
||||
$user_path = "Environment"
|
||||
|
||||
# list/arraylist methods don't allow IEqualityComparer override for case/backslash/quote-insensitivity, roll our own search
|
||||
Function Get-IndexOfPathElement ($list, [string]$value) {
|
||||
$idx = 0
|
||||
$value = $value.Trim('"').Trim('\')
|
||||
ForEach($el in $list) {
|
||||
If ([string]$el.Trim('"').Trim('\') -ieq $value) {
|
||||
return $idx
|
||||
}
|
||||
|
||||
$idx++
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
# alters list in place, returns true if at least one element was added
|
||||
Function Add-Elements ($existing_elements, $elements_to_add) {
|
||||
$last_idx = -1
|
||||
$changed = $false
|
||||
|
||||
ForEach($el in $elements_to_add) {
|
||||
$idx = Get-IndexOfPathElement $existing_elements $el
|
||||
|
||||
# add missing elements at the end
|
||||
If ($idx -eq -1) {
|
||||
$last_idx = $existing_elements.Add($el)
|
||||
$changed = $true
|
||||
}
|
||||
ElseIf ($idx -lt $last_idx) {
|
||||
$existing_elements.RemoveAt($idx) | Out-Null
|
||||
$existing_elements.Add($el) | Out-Null
|
||||
$last_idx = $existing_elements.Count - 1
|
||||
$changed = $true
|
||||
}
|
||||
Else {
|
||||
$last_idx = $idx
|
||||
}
|
||||
}
|
||||
|
||||
return $changed
|
||||
}
|
||||
|
||||
# alters list in place, returns true if at least one element was removed
|
||||
Function Remove-Elements ($existing_elements, $elements_to_remove) {
|
||||
$count = $existing_elements.Count
|
||||
|
||||
ForEach($el in $elements_to_remove) {
|
||||
$idx = Get-IndexOfPathElement $existing_elements $el
|
||||
$result.removed_idx = $idx
|
||||
If ($idx -gt -1) {
|
||||
$existing_elements.RemoveAt($idx)
|
||||
}
|
||||
}
|
||||
|
||||
return $count -ne $existing_elements.Count
|
||||
}
|
||||
|
||||
# PS registry provider doesn't allow access to unexpanded REG_EXPAND_SZ; fall back to .NET
|
||||
Function Get-RawPathVar ($scope) {
|
||||
If ($scope -eq "user") {
|
||||
$env_key = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey($user_path)
|
||||
}
|
||||
ElseIf ($scope -eq "machine") {
|
||||
$env_key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($system_path)
|
||||
}
|
||||
|
||||
return $env_key.GetValue($var_name, "", [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
|
||||
}
|
||||
|
||||
Function Set-RawPathVar($path_value, $scope) {
|
||||
If ($scope -eq "user") {
|
||||
$var_path = "HKCU:\" + $user_path
|
||||
}
|
||||
ElseIf ($scope -eq "machine") {
|
||||
$var_path = "HKLM:\" + $system_path
|
||||
}
|
||||
|
||||
Set-ItemProperty $var_path -Name $var_name -Value $path_value -Type ExpandString | Out-Null
|
||||
|
||||
return $path_value
|
||||
}
|
||||
|
||||
$parsed_args = Parse-Args $args -supports_check_mode $true
|
||||
|
||||
$result = @{changed=$false}
|
||||
|
||||
$var_name = Get-AnsibleParam $parsed_args "name" -Default "PATH"
|
||||
$elements = Get-AnsibleParam $parsed_args "elements" -FailIfEmpty $result
|
||||
$state = Get-AnsibleParam $parsed_args "state" -Default "present" -ValidateSet "present","absent"
|
||||
$scope = Get-AnsibleParam $parsed_args "scope" -Default "machine" -ValidateSet "machine","user"
|
||||
|
||||
$check_mode = Get-AnsibleParam $parsed_args "_ansible_check_mode" -Default $false
|
||||
|
||||
If ($elements -is [string]) {
|
||||
$elements = @($elements)
|
||||
}
|
||||
|
||||
If ($elements -isnot [Array]) {
|
||||
Fail-Json $result "elements must be a string or list of path strings"
|
||||
}
|
||||
|
||||
$current_value = Get-RawPathVar $scope
|
||||
$result.path_value = $current_value
|
||||
|
||||
# TODO: test case-canonicalization on wacky unicode values (eg turkish i)
|
||||
# TODO: detect and warn/fail on unparseable path? (eg, unbalanced quotes, invalid path chars)
|
||||
# TODO: detect and warn/fail if system path and Powershell isn't on it?
|
||||
|
||||
$existing_elements = New-Object System.Collections.ArrayList
|
||||
|
||||
# split on semicolons, accounting for quoted values with embedded semicolons (which may or may not be wrapped in whitespace)
|
||||
$pathsplit_re = [regex] '((?<q>\s*"[^"]+"\s*)|(?<q>[^;]+))(;$|$|;)'
|
||||
|
||||
ForEach ($m in $pathsplit_re.Matches($current_value)) {
|
||||
$existing_elements.Add($m.Groups['q'].Value) | Out-Null
|
||||
}
|
||||
|
||||
If ($state -eq "absent") {
|
||||
$result.changed = Remove-Elements $existing_elements $elements
|
||||
}
|
||||
ElseIf ($state -eq "present") {
|
||||
$result.changed = Add-Elements $existing_elements $elements
|
||||
}
|
||||
|
||||
# calculate the new path value from the existing elements
|
||||
$path_value = [String]::Join(";", $existing_elements.ToArray())
|
||||
$result.path_value = $path_value
|
||||
|
||||
If ($result.changed -and -not $check_mode) {
|
||||
Set-RawPathVar $path_value $scope | Out-Null
|
||||
}
|
||||
|
||||
Exit-Json $result
|
@ -1,79 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2016, Red Hat | Ansible
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# This is a windows documentation stub. Actual code lives in the .ps1
|
||||
# file of the same name
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'core'}
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: win_path
|
||||
version_added: "2.3"
|
||||
short_description: Manage Windows path environment variables
|
||||
description:
|
||||
- Allows element-based ordering, addition, and removal of Windows path environment variables.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Target path environment variable name.
|
||||
type: str
|
||||
default: PATH
|
||||
elements:
|
||||
description:
|
||||
- A single path element, or a list of path elements (ie, directories) to add or remove.
|
||||
- When multiple elements are included in the list (and C(state) is C(present)), the elements are guaranteed to appear in the same relative order
|
||||
in the resultant path value.
|
||||
- Variable expansions (eg, C(%VARNAME%)) are allowed, and are stored unexpanded in the target path element.
|
||||
- Any existing path elements not mentioned in C(elements) are always preserved in their current order.
|
||||
- New path elements are appended to the path, and existing path elements may be moved closer to the end to satisfy the requested ordering.
|
||||
- Paths are compared in a case-insensitive fashion, and trailing backslashes are ignored for comparison purposes. However, note that trailing
|
||||
backslashes in YAML require quotes.
|
||||
type: list
|
||||
required: yes
|
||||
state:
|
||||
description:
|
||||
- Whether the path elements specified in C(elements) should be present or absent.
|
||||
type: str
|
||||
choices: [ absent, present ]
|
||||
scope:
|
||||
description:
|
||||
- The level at which the environment variable specified by C(name) should be managed (either for the current user or global machine scope).
|
||||
type: str
|
||||
choices: [ machine, user ]
|
||||
default: machine
|
||||
notes:
|
||||
- This module is for modifying individual elements of path-like
|
||||
environment variables. For general-purpose management of other
|
||||
environment vars, use the M(win_environment) module.
|
||||
- This module does not broadcast change events.
|
||||
This means that the minority of windows applications which can have
|
||||
their environment changed without restarting will not be notified and
|
||||
therefore will need restarting to pick up new environment settings.
|
||||
- User level environment variables will require an interactive user to
|
||||
log out and in again before they become available.
|
||||
seealso:
|
||||
- module: win_environment
|
||||
author:
|
||||
- Matt Davis (@nitzmahone)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Ensure that system32 and Powershell are present on the global system path, and in the specified order
|
||||
win_path:
|
||||
elements:
|
||||
- '%SystemRoot%\system32'
|
||||
- '%SystemRoot%\system32\WindowsPowerShell\v1.0'
|
||||
|
||||
- name: Ensure that C:\Program Files\MyJavaThing is not on the current user's CLASSPATH
|
||||
win_path:
|
||||
name: CLASSPATH
|
||||
elements: C:\Program Files\MyJavaThing
|
||||
scope: user
|
||||
state: absent
|
||||
'''
|
@ -1,67 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2017, Dag Wieers <dag@wieers.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: win_tempfile
|
||||
version_added: "2.3"
|
||||
short_description: Creates temporary files and directories
|
||||
description:
|
||||
- Creates temporary files and directories.
|
||||
- For non-Windows targets, please use the M(tempfile) module instead.
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- Whether to create file or directory.
|
||||
type: str
|
||||
choices: [ directory, file ]
|
||||
default: file
|
||||
path:
|
||||
description:
|
||||
- Location where temporary file or directory should be created.
|
||||
- If path is not specified default system temporary directory (%TEMP%) will be used.
|
||||
type: path
|
||||
default: '%TEMP%'
|
||||
aliases: [ dest ]
|
||||
prefix:
|
||||
description:
|
||||
- Prefix of file/directory name created by module.
|
||||
type: str
|
||||
default: ansible.
|
||||
suffix:
|
||||
description:
|
||||
- Suffix of file/directory name created by module.
|
||||
type: str
|
||||
default: ''
|
||||
seealso:
|
||||
- module: tempfile
|
||||
author:
|
||||
- Dag Wieers (@dagwieers)
|
||||
'''
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Create temporary build directory
|
||||
win_tempfile:
|
||||
state: directory
|
||||
suffix: build
|
||||
|
||||
- name: Create temporary file
|
||||
win_tempfile:
|
||||
state: file
|
||||
suffix: temp
|
||||
"""
|
||||
|
||||
RETURN = r'''
|
||||
path:
|
||||
description: The absolute path to the created file or directory.
|
||||
returned: success
|
||||
type: str
|
||||
sample: C:\Users\Administrator\AppData\Local\Temp\ansible.bMlvdk
|
||||
'''
|
@ -1,66 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# this is a virtual module that is entirely implemented server side
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['stableinterface'],
|
||||
'supported_by': 'core'}
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: win_template
|
||||
version_added: "1.9.2"
|
||||
short_description: Template a file out to a remote server
|
||||
options:
|
||||
backup:
|
||||
description:
|
||||
- Determine whether a backup should be created.
|
||||
- When set to C(yes), create a backup file including the timestamp information
|
||||
so you can get the original file back if you somehow clobbered it incorrectly.
|
||||
type: bool
|
||||
default: no
|
||||
version_added: '2.8'
|
||||
newline_sequence:
|
||||
default: '\r\n'
|
||||
force:
|
||||
version_added: '2.4'
|
||||
notes:
|
||||
- Beware fetching files from windows machines when creating templates because certain tools, such as Powershell ISE,
|
||||
and regedit's export facility add a Byte Order Mark as the first character of the file, which can cause tracebacks.
|
||||
- You can use the M(win_copy) module with the C(content:) option if you prefer the template inline, as part of the
|
||||
playbook.
|
||||
- For Linux you can use M(template) which uses '\\n' as C(newline_sequence) by default.
|
||||
seealso:
|
||||
- module: win_copy
|
||||
- module: copy
|
||||
- module: template
|
||||
author:
|
||||
- Jon Hawkesworth (@jhawkesworth)
|
||||
extends_documentation_fragment:
|
||||
- template_common
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Create a file from a Jinja2 template
|
||||
win_template:
|
||||
src: /mytemplates/file.conf.j2
|
||||
dest: C:\Temp\file.conf
|
||||
|
||||
- name: Create a Unix-style file from a Jinja2 template
|
||||
win_template:
|
||||
src: unix/config.conf.j2
|
||||
dest: C:\share\unix\config.conf
|
||||
newline_sequence: '\n'
|
||||
backup: yes
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
backup_file:
|
||||
description: Name of the backup file that was created.
|
||||
returned: if backup=yes
|
||||
type: str
|
||||
sample: C:\Path\To\File.txt.11540.20150212-220915.bak
|
||||
'''
|
Loading…
Reference in New Issue