Windows module: win_optional_feature for Windows workstations (#53709)

* initial commit

* fix execute and \r\n

* \r attempt 2

* updated with integration tests and using new csharp import

* Apply suggestions from code review

Co-Authored-By: rcanderson23 <rcanderson23@gmail.com>

* fixed small docuement inaccuracies wrt returns

* removal of state in feature result

* removal of rc

* small fixes suggested in code review

* fixed variable assigning to result

* addition of comments on conditionals for clarity on matching

* swap logic of check_mode

* set $reboot_required so it is always returned

* removal of extraneous return information

* addition of integration tests

* set installation of parent features to true

* remove 2008 from tests

* changed test for TelnetClient from NetFx3

* change of tabs to spaces

* Add test check for OS version
This commit is contained in:
Carson Anderson 2019-03-19 20:13:06 -06:00 committed by Jordan Borean
parent f5f4948480
commit 31ceba7fd8
5 changed files with 270 additions and 0 deletions

View file

@ -0,0 +1,71 @@
#!powershell
# Copyright: (c) 2019, Carson Anderson <rcanderson23@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#AnsibleRequires -CSharpUtil Ansible.Basic
$spec = @{
options = @{
name = @{ type = "str"; required = $true }
state = @{ type = "str"; default = "present"; choices = @("absent", "present") }
source = @{ type = "str" }
include_parent = @{ type = "bool"; default = $false }
}
supports_check_mode = $true
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$name = $module.Params.name
$state = $module.Params.state
$source = $module.Params.source
$include_parent = $module.Params.include_parent
$module.Result.reboot_required = $false
if (-not (Get-Command -Name Enable-WindowsOptionalFeature -ErrorAction SilentlyContinue)) {
$module.FailJson("This version of Windows does not support the Enable-WindowsOptionalFeature.")
}
$feature_state_start = Get-WindowsOptionalFeature -Online -FeatureName $name
if (-not $feature_state_start) {
$module.FailJson("Failed to find feature '$name'")
}
if ($state -eq "present") {
# Matches for "Enabled" and "EnabledPending"
if ($feature_state_start.State -notlike "Enabled*") {
$install_args = @{
FeatureName = $name
All = $include_parent
}
if ($source) {
if (-not (Test-Path -LiteralPath $source)) {
$module.FailJson("Path could not be found '$source'")
}
$install_args.Source = $source
}
if (-not $module.CheckMode) {
$action_result = Enable-WindowsOptionalFeature -Online -NoRestart @install_args
$module.Result.reboot_required = $action_result.RestartNeeded
}
$module.Result.changed = $true
}
} else {
# Matches for Disabled, DisabledPending, and DisabledWithPayloadRemoved
if ($feature_state_start.State -notlike "Disabled*") {
$remove_args = @{
FeatureName = $name
}
if (-not $module.CheckMode) {
$action_result = Disable-WindowsOptionalFeature -Online -NoRestart @remove_args
$module.Result.reboot_required = $action_result.RestartNeeded
}
$module.Result.changed = $true
}
}
$module.ExitJson()

View file

@ -0,0 +1,83 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Carson Anderson <rcanderson23@gmail.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_optional_feature
version_added: "2.8"
short_description: Manage optional Windows features
description:
- Install or uninstall optional Windows features on non-Server Windows.
- This module uses the C(Enable-WindowsOptionalFeature) and C(Disable-WindowsOptionalFeature) cmdlets.
options:
name:
description:
- The name of the feature to install.
- This relates to C(FeatureName) in the Powershell cmdlet.
- To list all available features use the PowerShell command C(Get-WindowsOptionalFeature).
type: str
required: yes
state:
description:
- Whether to ensure the feature is absent or present on the system.
type: str
choices: [ absent, present ]
default: present
include_parent:
description:
- Whether to enable the parent feature and the parent's dependencies.
type: bool
default: no
source:
description:
- Specify a source to install the feature from.
- Can either be C({driveletter}:\sources\sxs) or C(\\{IP}\share\sources\sxs).
type: str
seealso:
- module: win_chocolatey
- module: win_feature
- module: win_package
author:
- Carson Anderson (@rcanderson23)
'''
EXAMPLES = r'''
- name: Install .Net 3.5
win_optional_feature:
name: NetFx3
state: present
- name: Install .Net 3.5 from source
win_optional_feature:
name: NetFx3
source: \\share01\win10\sources\sxs
state: present
- name: Install Microsoft Subsystem for Linux
win_optional_feature:
name: Microsoft-Windows-Subsystem-Linux
state: present
register: wsl_status
- name: Reboot if installing Linux Subsytem as feature requires it
win_reboot:
when: wsl_status.reboot_required
'''
RETURN = r'''
reboot_required:
description: True when the target server requires a reboot to complete updates
returned: success
type: bool
sample: true
'''

View file

@ -0,0 +1,3 @@
shippable/windows/group2
skip/windows/2008
skip/windows/2008-R2

View file

@ -0,0 +1,25 @@
# Test code for win_optional_feature module
# Copyright: (c) 2019, Carson Anderson <rcanderson23@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#
# 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 <http://www.gnu.org/licenses/>.
- name: check if host supports module
win_shell: if (Get-Command -Name Enable-WindowsOptionalFeature -ErrorAction SilentlyContinue) { $true } else { $false }
register: run_tests
- name: run tests
include_tasks: tests.yml
when: run_tests.stdout | trim | bool

View file

@ -0,0 +1,88 @@
# Test code for win_optional_feature module
# Copyright: (c) 2019, Carson Anderson <rcanderson23@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#
# 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 <http://www.gnu.org/licenses/>.
- name: run with check_mode
win_optional_feature:
name: TelnetClient
state: present
include_parent: true
check_mode: yes
register: feature_check
- name: assert check_mode
assert:
that:
- feature_check.changed
- name: run without check_mode
win_optional_feature:
name: TelnetClient
state: present
include_parent: true
register: real_feature_check
- name: assert feature installed
assert:
that:
- real_feature_check.changed
- name: test idempotence for install
win_optional_feature:
name: TelnetClient
state: present
include_parent: true
register: real_feature_check
- name: assert idempotence
assert:
that:
- not real_feature_check.changed
- name: removal run with check_mode
win_optional_feature:
name: TelnetClient
state: absent
check_mode: yes
register: feature_check
- name: assert removal check_mode
assert:
that:
- feature_check.changed
- name: remove feature
win_optional_feature:
name: TelnetClient
state: absent
register: real_feature_check
- name: assert feature removed
assert:
that:
- real_feature_check.changed
- name: test idempotence for removal
win_optional_feature:
name: TelnetClient
state: absent
register: real_feature_check
- name: assert idempotence
assert:
that:
- not real_feature_check.changed