Update win_user module to support more user options and group membership changes.

reviewable/pr18780/r1
Chris Church 10 years ago
parent db5668b84c
commit 3a40d79cff

@ -20,6 +20,9 @@
# POWERSHELL_COMMON # POWERSHELL_COMMON
######## ########
$ADS_UF_PASSWD_CANT_CHANGE = 64
$ADS_UF_DONT_EXPIRE_PASSWD = 65536
$adsi = [ADSI]"WinNT://$env:COMPUTERNAME" $adsi = [ADSI]"WinNT://$env:COMPUTERNAME"
function Get-User($user) { function Get-User($user) {
@ -27,22 +30,23 @@ function Get-User($user) {
return return
} }
function Create-User([string]$user, [string]$passwd) { function Get-UserFlag($user, $flag) {
$adsiuser = $adsi.Create("User", $user) If ($user.UserFlags[0] -band $flag) {
$adsiuser.SetPassword($passwd) $true
$adsiuser.SetInfo() }
$adsiuser Else {
return $false
}
} }
function Update-Password($user, [string]$passwd) { function Set-UserFlag($user, $flag) {
$user.SetPassword($passwd) $user.UserFlags = ($user.UserFlags[0] -BOR $flag)
$user.SetInfo()
} }
function Delete-User($user) { function Clear-UserFlag($user, $flag) {
$adsi.delete("user", $user.Name.Value) $user.UserFlags = ($user.UserFlags[0] -BXOR $flag)
} }
######## ########
$params = Parse-Args $args; $params = Parse-Args $args;
@ -51,56 +55,193 @@ $result = New-Object psobject @{
changed = $false changed = $false
}; };
If (-not $params.name.GetType) If (-not $params.name.GetType) {
{
Fail-Json $result "missing required arguments: name" Fail-Json $result "missing required arguments: name"
} }
$username = Get-Attr $params "name"
$fullname = Get-Attr $params "fullname"
$description = Get-Attr $params "description"
$password = Get-Attr $params "password"
If ($params.state) { If ($params.state) {
$state = $params.state.ToString().ToLower() $state = $params.state.ToString().ToLower()
If (($state -ne 'present') -and ($state -ne 'absent')) { If (($state -ne 'present') -and ($state -ne 'absent') -and ($state -ne 'query')) {
Fail-Json $result "state is '$state'; must be 'present' or 'absent'" Fail-Json $result "state is '$state'; must be 'present', 'absent' or 'query'"
} }
} }
Elseif (!$params.state) { ElseIf (!$params.state) {
$state = "present" $state = "present"
} }
If ((-not $params.password.GetType) -and ($state -eq 'present')) If ($params.update_password) {
{ $update_password = $params.update_password.ToString().ToLower()
Fail-Json $result "missing required arguments: password" If (($update_password -ne 'always') -and ($update_password -ne 'on_create')) {
Fail-Json $result "update_password is '$update_password'; must be 'always' or 'on_create'"
}
}
ElseIf (!$params.update_password) {
$update_password = "always"
} }
$username = Get-Attr $params "name" $password_expired = Get-Attr $params "password_expired" $null
$password = Get-Attr $params "password" If ($password_expired -ne $null) {
$password_expired = $password_expired | ConvertTo-Bool
}
$password_never_expires = Get-Attr $params "password_never_expires" $null
If ($password_never_expires -ne $null) {
$password_never_expires = $password_never_expires | ConvertTo-Bool
}
$user_cannot_change_password = Get-Attr $params "user_cannot_change_password" $null
If ($user_cannot_change_password -ne $null) {
$user_cannot_change_password = $user_cannot_change_password | ConvertTo-Bool
}
$account_disabled = Get-Attr $params "account_disabled" $null
If ($account_disabled -ne $null) {
$account_disabled = $account_disabled | ConvertTo-Bool
}
$account_locked = Get-Attr $params "account_locked" $null
If ($account_locked -ne $null) {
$account_locked = $account_locked | ConvertTo-Bool
if ($account_locked) {
Fail-Json $result "account_locked must be set to 'no' if provided"
}
}
$groups = Get-Attr $params "groups" $null
If ($groups -ne $null) {
If ($groups.GetType().Name -eq "String") {
[string[]]$groups = $groups.Split(",")
}
ElseIf ($groups.GetType().Name -ne "Object[]") {
Fail-Json $result "groups must be a string or array"
}
$groups = $groups | ForEach { ([string]$_).Trim() } | Where { $_ }
If ($groups -eq $null) {
$groups = @()
}
}
If ($params.groups_action) {
$groups_action = $params.groups_action.ToString().ToLower()
If (($groups_action -ne 'replace') -and ($groups_action -ne 'add') -and ($groups_action -ne 'remove')) {
Fail-Json $result "groups_action is '$groups_action'; must be 'replace', 'add' or 'remove'"
}
}
ElseIf (!$params.groups_action) {
$groups_action = "replace"
}
$user_obj = Get-User $username $user_obj = Get-User $username
if ($state -eq 'present') { If ($state -eq 'present') {
# Add or update user # Add or update user
try { try {
if ($user_obj.GetType) { If (!$user_obj.GetType) {
Update-Password $user_obj $password $user_obj = $adsi.Create("User", $username)
If ($password -ne $null) {
$user_obj.SetPassword($password)
}
$result.changed = $true
}
ElseIf (($password -ne $null) -and ($update_password -eq 'always')) {
[void][system.reflection.assembly]::LoadWithPartialName('System.DirectoryServices.AccountManagement')
$pc = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext 'Machine', $env:COMPUTERNAME
# FIXME: ValidateCredentials fails if PasswordExpired == 1
If (!$pc.ValidateCredentials($username, $password)) {
$user_obj.SetPassword($password)
$result.changed = $true
}
}
If (($fullname -ne $null) -and ($fullname -ne $user_obj.FullName[0])) {
$user_obj.FullName = $fullname
$result.changed = $true
}
If (($description -ne $null) -and ($description -ne $user_obj.Description[0])) {
$user_obj.Description = $description
$result.changed = $true
}
If (($password_expired -ne $null) -and ($password_expired -ne ($user_obj.PasswordExpired | ConvertTo-Bool))) {
$user_obj.PasswordExpired = If ($password_expired) { 1 } Else { 0 }
$result.changed = $true
}
If (($password_never_expires -ne $null) -and ($password_never_expires -ne (Get-UserFlag $user_obj $ADS_UF_DONT_EXPIRE_PASSWD))) {
If ($password_never_expires) {
Set-UserFlag $user_obj $ADS_UF_DONT_EXPIRE_PASSWD
}
Else {
Clear-UserFlag $user_obj $ADS_UF_DONT_EXPIRE_PASSWD
}
$result.changed = $true
} }
else { If (($user_cannot_change_password -ne $null) -and ($user_cannot_change_password -ne (Get-UserFlag $user_obj $ADS_UF_PASSWD_CANT_CHANGE))) {
Create-User $username $password If ($user_cannot_change_password) {
Set-UserFlag $user_obj $ADS_UF_PASSWD_CANT_CHANGE
}
Else {
Clear-UserFlag $user_obj $ADS_UF_PASSWD_CANT_CHANGE
}
$result.changed = $true
}
If (($account_disabled -ne $null) -and ($account_disabled -ne $user_obj.AccountDisabled)) {
$user_obj.AccountDisabled = $account_disabled
$result.changed = $true
}
If (($account_locked -ne $null) -and ($account_locked -ne $user_obj.IsAccountLocked)) {
$user_obj.IsAccountLocked = $account_locked
$result.changed = $true
}
If ($groups.GetType) {
[string[]]$current_groups = $user_obj.Groups() | ForEach { $_.GetType().InvokeMember("Name", "GetProperty", $null, $_, $null) }
If (($groups_action -eq "remove") -or ($groups_action -eq "replace")) {
ForEach ($grp in $current_groups) {
If ((($groups_action -eq "remove") -and ($groups -contains $grp)) -or (($groups_action -eq "replace") -and ($groups -notcontains $grp))) {
$group_obj = $adsi.Children | where { $_.SchemaClassName -eq 'Group' -and $_.Name -eq $grp }
If ($group_obj.GetType) {
$group_obj.Remove($user_obj.Path)
$result.changed = $true
}
Else {
Fail-Json $result "group '$grp' not found"
}
}
}
}
If (($groups_action -eq "add") -or ($groups_action -eq "replace")) {
ForEach ($grp in $groups) {
If ($current_groups -notcontains $grp) {
$group_obj = $adsi.Children | where { $_.SchemaClassName -eq 'Group' -and $_.Name -eq $grp }
If ($group_obj.GetType) {
$group_obj.Add($user_obj.Path)
$result.changed = $true
}
Else {
Fail-Json $result "group '$grp' not found"
}
}
}
}
}
If ($result.changed) {
$user_obj.SetInfo()
} }
$result.changed = $true
$user_obj = Get-User $username
} }
catch { catch {
Fail-Json $result $_.Exception.Message Fail-Json $result $_.Exception.Message
} }
} }
else { ElseIf ($state -eq 'absent') {
# Remove user # Remove user
try { try {
if ($user_obj.GetType) { If ($user_obj.GetType) {
Delete-User $user_obj $username = $user_obj.Name.Value
$adsi.delete("User", $user_obj.Name.Value)
$result.changed = $true $result.changed = $true
} $user_obj = $null
else {
Set-Attr $result "msg" "User '$username' was not found"
} }
} }
catch { catch {
@ -108,9 +249,38 @@ else {
} }
} }
# Set-Attr $result "user" $user_obj try {
Set-Attr $result "user_name" $user_obj.Name If ($user_obj.GetType) {
Set-Attr $result "user_fullname" $user_obj.FullName $user_obj.RefreshCache()
Set-Attr $result "user_path" $user_obj.Path Set-Attr $result "name" $user_obj.Name[0]
Set-Attr $result "fullname" $user_obj.FullName[0]
Set-Attr $result "path" $user_obj.Path
Set-Attr $result "description" $user_obj.Description[0]
Set-Attr $result "password_expired" ($user_obj.PasswordExpired | ConvertTo-Bool)
Set-Attr $result "password_never_expires" (Get-UserFlag $user_obj $ADS_UF_DONT_EXPIRE_PASSWD)
Set-Attr $result "user_cannot_change_password" (Get-UserFlag $user_obj $ADS_UF_PASSWD_CANT_CHANGE)
Set-Attr $result "account_disabled" $user_obj.AccountDisabled
Set-Attr $result "account_locked" $user_obj.IsAccountLocked
Set-Attr $result "sid" (New-Object System.Security.Principal.SecurityIdentifier($user_obj.ObjectSid.Value, 0)).Value
$user_groups = @()
ForEach ($grp in $user_obj.Groups()) {
$group_result = New-Object psobject @{
name = $grp.GetType().InvokeMember("Name", "GetProperty", $null, $grp, $null)
path = $grp.GetType().InvokeMember("ADsPath", "GetProperty", $null, $grp, $null)
}
$user_groups += $group_result;
}
Set-Attr $result "groups" $user_groups
Set-Attr $result "state" "present"
}
Else {
Set-Attr $result "name" $username
Set-Attr $result "msg" "User '$username' was not found"
Set-Attr $result "state" "absent"
}
}
catch {
Fail-Json $result $_.Exception.Message
}
Exit-Json $result; Exit-Json $result

@ -31,32 +31,109 @@ description:
options: options:
name: name:
description: description:
- Username of the user to manage - Name of the user to create, remove or modify.
required: true required: true
fullname:
description:
- Full name of the user
required: false
default: null default: null
aliases: [] version_added: "1.8"
description:
description:
- Description of the user
required: false
default: null
version_added: "1.8"
password: password:
description: description:
- Password for the user (plain text) - Optionally set the user's password to this (plain text) value.
required: true required: false
default: null default: null
aliases: [] update_password:
description:
- C(always) will update passwords if they differ. C(on_create) will
only set the password for newly created users.
required: false
choices: [ 'always', 'on_create' ]
default: always
version_added: "1.8"
password_expired:
description:
- C(yes) will require the user to change their password at next login.
C(no) will clear the expired password flag.
required: false
choices: [ 'yes', 'no' ]
default: null
version_added: "1.8"
password_never_expires:
description:
- C(yes) will set the password to never expire. C(no) will allow the
password to expire.
required: false
choices: [ 'yes', 'no' ]
default: null
version_added: "1.8"
user_cannot_change_password:
description:
- C(yes) will prevent the user from changing their password. C(no) will
allow the user to change their password.
required: false
choices: [ 'yes', 'no' ]
default: null
version_added: "1.8"
account_disabled:
description:
- C(yes) will disable the user account. C(no) will clear the disabled
flag.
required: false
choices: [ 'yes', 'no' ]
default: null
version_added: "1.8"
account_locked:
description:
- C(no) will unlock the user account if locked.
required: false
choices: [ 'no' ]
default: null
version_added: "1.8"
groups:
description:
- Adds or removes the user from this comma-separated lis of groups,
depending on the value of I(groups_action). When I(groups_action) is
C(replace) and I(groups) is set to the empty string ('groups='), the
user is removed from all groups.
required: false
version_added: "1.8"
groups_action:
description:
- If C(replace), the user is added as a member of each group in
I(groups) and removed from any other groups. If C(add), the user is
added to each group in I(groups) where not already a member. If
C(remove), the user is removed from each group in I(groups).
required: false
choices: [ "replace", "add", "remove" ]
default: "replace"
version_added: "1.8"
state: state:
description: description:
- Whether to create or delete a user - When C(present), creates or updates the user account. When C(absent),
removes the user account if it exists. When C(query) (new in 1.8),
retrieves the user account details without making any changes.
required: false required: false
choices: choices:
- present - present
- absent - absent
- query
default: present default: present
aliases: [] aliases: []
author: Paul Durivage author: Paul Durivage / Chris Church
''' '''
EXAMPLES = ''' EXAMPLES = '''
# Ad-hoc example # Ad-hoc example
$ ansible -i hosts -m win_user -a "name=bob password=Password12345" all $ ansible -i hosts -m win_user -a "name=bob password=Password12345 groups=Users" all
$ ansible -i hosts -m win_user -a "name=bob password=Password12345 state=absent" all $ ansible -i hosts -m win_user -a "name=bob state=absent" all
# Playbook example # Playbook example
--- ---
@ -68,4 +145,5 @@ $ ansible -i hosts -m win_user -a "name=bob password=Password12345 state=absent"
win_user: win_user:
name: ansible name: ansible
password: "@ns1bl3" password: "@ns1bl3"
groups: ["Users"]
''' '''

Loading…
Cancel
Save