Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New module: Add module or managing Windows Active Directory users (windows/win_domain_user) #24075

Merged
merged 11 commits into from
Aug 11, 2017

Conversation

nwchandler
Copy link
Contributor

SUMMARY

This PR adds support for managing Windows Active Directory users with Ansible. The existing win_user module is designed to support management of local users. There are enough differences between local user management and domain user management that a separate module seemed to make sense. See discussion on Issue #20428.

ISSUE TYPE
  • New Module Pull Request
COMPONENT NAME

windows/win_domain_user.ps1

ANSIBLE VERSION
ansible 2.3.0.0
  config file =
  configured module search path = Default w/o overrides
  python version = 2.7.12 (default, Jun 29 2016, 14:05:02) [GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)]
ADDITIONAL INFORMATION

@ansibot ansibot added affects_2.4 This issue/PR affects Ansible v2.4 community_review In order to be merged, this PR must follow the community review workflow. module This issue/PR relates to a module. needs_triage Needs a first human triage before being processed. new_module This PR includes a new module. new_plugin This PR includes a new plugin. windows Windows community labels Apr 27, 2017
@ansibot
Copy link
Contributor

ansibot commented Apr 27, 2017

The test ansible-test sanity --test validate-modules failed with the following error:

lib/ansible/modules/windows/win_domain_user.py:0:0: E312 No RETURN documentation provided

click here for bot help

@ansibot ansibot added ci_verified Changes made in this PR are causing tests to fail. needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR. and removed community_review In order to be merged, this PR must follow the community review workflow. labels Apr 27, 2017
@Qalthos Qalthos removed needs_triage Needs a first human triage before being processed. new_plugin This PR includes a new plugin. labels Apr 27, 2017
@ansibot ansibot added community_review In order to be merged, this PR must follow the community review workflow. and removed ci_verified Changes made in this PR are causing tests to fail. needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR. labels Apr 27, 2017
# POWERSHELL_COMMON

########
Import-Module ActiveDirectory
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this line will fail if run against a non-active directory server. Consider a try catch and fail-json if the ActiveDirectory module is not available.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, for the review! Good idea. I will make the update.

@ansibot ansibot added the stale_ci This PR has been tested by CI more than one week ago. Close and re-open this PR to get it retested. label May 9, 2017
Copy link
Contributor

@jborean93 jborean93 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a great module to have, just a few questions and queries.

}

# Parameter validation
If ($account_locked -ne $null -and $account_locked) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My brain isn't working right now are you able to explain this logic? To me it seems like it will fail if $account_locked is set to true if it has been set? Why can't we create a locked account?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed Microsoft's lead on this one. There is an Unlock-ADAccount cmdlet, but no corresponding Lock-ADAccount. The thinking is that an account would be locked because a user tried to login too many times with incorrect credentials, so an admin would unlock an account, but not lock it. If an account needed to be "locked" by an admin, he/she would just disable the account by setting "enabled" to false.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries that makes sense, I can see the same functionality in the win_user module this was just me being ignorant :)

If ($password -and (($new_user -and $update_password -eq "on_create") -or $update_password -eq "always")) {
$secure_password = ConvertTo-SecureString $password -AsPlainText -Force
Set-ADAccountPassword -Identity $username -Reset:$true -Confirm:$false -NewPassword $secure_password
$user_obj = Get-ADUser -Identity $username -Properties *
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just run this once after creating the user to save getting these details everytime we want to make a change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I did initially, but I found problems in testing. As accounts were updated, I ran into problems where later logic would fail. I ended up deciding it was better to refresh the state of the user object after every update.
(Sorry, I know that's vague; I don't recall the specific issues I ran into off-hand.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, sounds like a typical Microsoft thing to me.

$password_never_expires = Get-AnsibleParam -obj $params -name "password_never_expires" -type "bool"
$user_cannot_change_password = Get-AnsibleParam -obj $params -name "user_cannot_change_password" -type "bool"
$account_locked = Get-AnsibleParam -obj $params -name "account_locked" -type "bool"
$groups = Get-AnsibleParam -obj $params -name "groups"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is now a list type that will automatically convert a the item to a list if it isn't already.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, @jborean93 - My apologies for the late reply, but thank you for the comments. Good to know about the list type; I'll update this.


# Configure group assignment
If ($groups -ne $null) {
If ($groups -is [System.String]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If using the list type it will automatically convert it to a list and split it by , if it is a string.



DOCUMENTATION = r'''
---
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a notes section around where this module can run and the requirements?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'll add that.

@ansibot ansibot added needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR. and removed community_review In order to be merged, this PR must follow the community review workflow. labels May 13, 2017
@alikins alikins changed the title Initial win_domain_user module support New module: Add module or managing Windows Active Directory users (windows/win_domain_user) May 22, 2017
@nathanwebsterdotme
Copy link
Contributor

Looking forward to this one!

@dagwieers
Copy link
Contributor

@nathanwebsterdotme If you do, I'd suggest you test it out in your environment and provide feedback. And help with code-review. Those things will speed up it being merged.

########

$result = @{
changed = $false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing semi-colon is not needed.

Fail-Json $result "Failed to import ActiveDirectory PowerShell module. This module should be run on a domain controller, and the ActiveDirectory module must be available."
}

$params = Parse-Args $args
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please look into adding check-mode support. Often you can get away with some strategically places -WhatIf:$check_mode statements !

- When C(present), creates or updates the user account. When C(absent),
removes the user account if it exists. When C(query),
retrieves the user account details without making any changes.
required: false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If parameters are not required, you don't have to add required: false. Only add required: true if they are required.

- C(no) will unlock the user account if locked.
required: false
choices: [ 'no' ]
default: null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed, the default value is null, so it's implicit.

description:
- C(no) will unlock the user account if locked.
required: false
choices: [ 'no' ]
Copy link
Contributor

@dagwieers dagwieers May 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't the choice be [ 'yes', 'no' ] as you'd expect from a boolean ?
As per previous remark, this would become:

    type: bool

description:
- C(yes) will enable the user account. C(no) will disable the account.
required: false
choices:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nowadays we do:

    type: bool
    default: 'yes'

version_added: "2.4"
short_description: Manages Windows Active Directory user accounts
description:
- Manages Windows Active Directory user accounts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing dot required. I know, quite pedantic rules and all.

default: null

author:
- "Nick Chandler (@nwchandler)"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We tend to not add quotes where they are no needed in YAML.
Here they are not needed.

@ansibot ansibot removed merge_commit This PR contains at least one merge commit. Please resolve! needs_rebase https://docs.ansible.com/ansible/devel/dev_guide/developing_rebasing.html labels Jul 27, 2017
@nwchandler
Copy link
Contributor Author

Hi, all. I believe I've addressed the comments in the new version of code (pending one last Shippable test). @dagwieers - I did some work on enabling check_mode, but it gave me some problems. I think it may take a little more re-work than I originally envisioned. So, I pushed the other requested changes. I can take another swing at it at some point, but it may be a while before I can, and I wanted to get the other updates in. I'll defer to you whether that would be a blocker or would be something we could add in the future.

DOCUMENTATION = r'''
---
module: win_domain_user
version_added: 2.4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a string:

version_added: '2.4'

- C(yes) will require the user to change their password at next login.
C(no) will clear the expired password flag. This is mutually exclusive
with I(password_never_expires).
choices: [ 'yes', 'no' ]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be:

type: bool

description:
- C(yes) will set the password to never expire. C(no) will allow the
password to expire. This is mutually exclusive with I(password_expired)
choices: [ 'yes', 'no' ]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be:

type: bool

description:
- C(yes) will prevent the user from changing their password. C(no) will
allow the user to change their password.
choices: [ 'yes', 'no' ]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be:

type: bool

# If the account does not exist, create it
If (-not $user_obj) {
If ($path -ne $null){
New-ADUser -Name $username -Path $path
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add here -WhatIf:$check_mode

New-ADUser -Name $username -Path $path
}
Else {
New-ADUser -Name $username
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add here -WhatIf:$check_mode

Else {
New-ADUser -Name $username
}
$user_obj = Get-ADUser -Identity $username -Properties *
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess, skip everything that follows on $check_mode (e.g. by running Exit-Json)


# Configure password policies
If (($password_never_expires -ne $null) -and ($password_never_expires -ne $user_obj.PasswordNeverExpires)) {
Set-ADUser -Identity $username -PasswordNeverExpires $password_never_expires
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add here -WhatIf:$check_mode

$result.changed = $true
}
If (($password_expired -ne $null) -and ($password_expired -ne $user_obj.PasswordExpired)) {
Set-ADUser -Identity $username -ChangePasswordAtLogon $password_expired
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add here -WhatIf:$check_mode

$result.changed = $true
}
If (($user_cannot_change_password -ne $null) -and ($user_cannot_change_password -ne $user_obj.CannotChangePassword)) {
Set-ADUser -Identity $username -CannotChangePassword $user_cannot_change_password
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add here -WhatIf:$check_mode

@dagwieers
Copy link
Contributor

@nwchandler check-mode is a must-have for every (new) module. It is best to first make it full-featured, then look at supporting check-mode. In this case, I think you can get away with adding -WhatIf:$check_mode to most statements that modify things (like Add-ADUser, Set-ADUser, ...) and leave out some of the post-change validation/lookups. Maybe terminate the module prematurely if needed (even if that means not everything can be checked properly). Check-mode is often best-effort (and heavily depends on low-level implementation details we have no control over).

Let me know if you need help.

@nwchandler
Copy link
Contributor Author

nwchandler commented Jul 28, 2017

Thanks for the feedback and ideas on check_mode support, @dagwieers. I have made some updates accordingly. Short-circuiting and exiting early is a good idea.

Copy link
Contributor

@jborean93 jborean93 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, just did some local tests and things seem to work correctly here. Keen to get this in and people testing it in real life situations.

@jborean93
Copy link
Contributor

shitpit

@nwchandler
Copy link
Contributor Author

Thanks for everyone's reviews and feedback!

@ansibot ansibot added stale_ci This PR has been tested by CI more than one week ago. Close and re-open this PR to get it retested. stale_review Updates were made after the last review and the last review is more than 7 days old. labels Aug 5, 2017
@dagwieers
Copy link
Contributor

dagwieers commented Aug 8, 2017

So the module freeze deadline for v2.4 is getting closer (2017-08-29). Now is the time to finish up, get it reviewed and merged with no delay.

@nwchandler
Copy link
Contributor Author

It looks like @jborean93 approved - anything else I need to do to make this happen, @dagwieers ?

@dagwieers
Copy link
Contributor

dagwieers commented Aug 8, 2017

@nwchandler If you're asking me specifically, the only thing I noted was the use of type: bool in the documentation. Ah, and now I see you did add it, but it should replace the choices: ['no', 'yes'] part.

@ansibot ansibot removed stale_ci This PR has been tested by CI more than one week ago. Close and re-open this PR to get it retested. stale_review Updates were made after the last review and the last review is more than 7 days old. labels Aug 11, 2017
@jborean93
Copy link
Contributor

Thanks for the work here @nwchandler, I'm going to merge this in so people can start using it in devel. Are you able to update https://github.com/ansible/ansible/blob/devel/.github/BOTMETA.yml and https://github.com/ansible/ansible/blob/devel/CHANGELOG.md with the relevant info for the new module.

@jborean93 jborean93 merged commit 22533c0 into ansible:devel Aug 11, 2017
@dagwieers
Copy link
Contributor

BOTMETA should not require updating if the author(s) are listed in the module.

@jborean93
Copy link
Contributor

oh did not know that, the more you know

@ansible ansible locked and limited conversation to collaborators Apr 26, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
affects_2.4 This issue/PR affects Ansible v2.4 module This issue/PR relates to a module. needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR. new_module This PR includes a new module. support:community This issue/PR relates to code supported by the Ansible community. support:core This issue/PR relates to code supported by the Ansible Engineering Team. windows Windows community
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants