Update win_user module to support more user options and group membership changes.
This commit is contained in:
parent
db5668b84c
commit
3a40d79cff
2 changed files with 304 additions and 56 deletions
|
@ -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) {
|
||||||
else {
|
$user_obj.SetPassword($password)
|
||||||
Create-User $username $password
|
}
|
||||||
}
|
|
||||||
$result.changed = $true
|
|
||||||
$user_obj = Get-User $username
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Fail-Json $result $_.Exception.Message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# Remove user
|
|
||||||
try {
|
|
||||||
if ($user_obj.GetType) {
|
|
||||||
Delete-User $user_obj
|
|
||||||
$result.changed = $true
|
$result.changed = $true
|
||||||
}
|
}
|
||||||
else {
|
ElseIf (($password -ne $null) -and ($update_password -eq 'always')) {
|
||||||
Set-Attr $result "msg" "User '$username' was not found"
|
[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
|
||||||
|
}
|
||||||
|
If (($user_cannot_change_password -ne $null) -and ($user_cannot_change_password -ne (Get-UserFlag $user_obj $ADS_UF_PASSWD_CANT_CHANGE))) {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Fail-Json $result $_.Exception.Message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ElseIf ($state -eq 'absent') {
|
||||||
|
# Remove user
|
||||||
|
try {
|
||||||
|
If ($user_obj.GetType) {
|
||||||
|
$username = $user_obj.Name.Value
|
||||||
|
$adsi.delete("User", $user_obj.Name.Value)
|
||||||
|
$result.changed = $true
|
||||||
|
$user_obj = $null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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…
Reference in a new issue