DscResources/Carbon_Group/Carbon_Group.psm1
# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. $psModulesPath = Join-Path -Path $PSScriptRoot -ChildPath '..\..' -Resolve Import-Module -Name (Join-Path -Path $psModulesPath -ChildPath 'Carbon' -Resolve) ` -Function @('Install-CGroup', 'Get-CGroup', 'Test-CGroup', 'Uninstall-CGroup') Import-Module -Name (Join-Path -Path $psModulesPath -ChildPath 'Carbon.Accounts' -Resolve) ` -Function @('Resolve-CIdentity', 'Resolve-CIdentityName') function Get-TargetResource { [CmdletBinding()] [OutputType([hashtable])] param ( [parameter(Mandatory = $true)] [System.String] $Name ) Set-StrictMode -Version 'Latest' $group = Get-CGroup -Name $Name -ErrorAction Ignore $ensure = 'Absent' $description = $null $members = @() if( $group ) { $description = $group.Description $members = @($group.Members | Resolve-PrincipalName) $ensure = 'Present' $group.Dispose() } @{ Name = $Name Ensure = $ensure Description = $description Members = $members } } function Set-TargetResource { <# .SYNOPSIS DSC resource for configuring local Windows groups. .DESCRIPTION The `Carbon_Group` resource installs and uninstalls groups. It can also modify members of existing groups. The group is installed when `Ensure` is set to `Present`. Group's members are updated based on the values of both `Members` and `EnsureMembers` properties, where `Members` lists users and `EnsureMemebers` controls how the list is used. If `EnsureMembers` is set to `Exact`, the group is configured to have the exact members specified. If set to `Present`, the specified members are added to the group if they are not members already. If set to `Absent`, the specified members are removed from the group if they are members of it. Defaults to `Exact`. Because DSC resources run under the LCM which runs as `System`, local system accounts must have access to the directories where both new and existing member accounts can be found. The group is removed when `Ensure` is set to `Absent`. When removing a group, all other properties are ignored. The `Carbon_Group` resource was added in Carbon 2.1.0. .LINK Add-CGroupMember .LINK Install-CGroup .LINK Remove-CGroupMember .LINK Test-CGroup .LINK Uninstall-CGroup .EXAMPLE > Demonstrates how to install a group and set its members. Carbon_Group 'CreateFirstOrder' { Name = 'FirstOrder'; Description = 'On to victory!'; Ensure = 'Present'; Members = @( 'FO\SupremeLeaderSnope', 'FO\KRen' ); } .EXAMPLE > Demonstrates how to uninstall a group. Carbon_Group 'RemoveRepublic { Name = 'Republic'; Ensure = 'Absent'; } .EXAMPLE > Demonstrates how to add members to an existing group. Carbon_Group 'AddVader' { Name = 'SithOrder'; Ensure = 'Present'; EnsureMembers = 'Present'; Members = @( 'SO\DarthVader' ); } .EXAMPLE > Demonstrates how to remove members from an existing group. Carbon_Group 'RemoveAnakin' { Name = 'JediOrder'; Ensure = 'Present'; EnsureMembers = 'Absent'; Members = @( 'JO\ASkywalker' ); } #> [CmdletBinding(SupportsShouldProcess)] param ( [parameter(Mandatory=$true)] [String] # The name of the group. $Name, [String] # A description of the group. Only used when adding/updating a group (i.e. when `Ensure` is `Present`). $Description, [ValidateSet("Present","Absent")] [String] # Should be either `Present` or `Absent`. If set to `Present`, a group is configured and membership configured. If set to `Absent`, the group is removed. $Ensure, # Controls how the list of users, given in the `Members` property, gets used. Should be either `Exact`, `Present` or `Absent`. If set to `Exact`, the group is configured to have the exact members specified. If set to `Present`, the specified members are added to the group if they are not members already. If set to `Absent`, the specified members are removed from the group if they are members of it. [ValidateSet('Exact','Present','Absent')] [String] $EnsureMembers = 'Exact', [string[]] # A list of users, which is used according to value of `EnsureMembers`. Only used when adding/updating a group (i.e. when `Ensure` is `Present`). $Members = @() ) Set-StrictMode -Version 'Latest' if( $Ensure -eq 'Absent' ) { Uninstall-CGroup -Name $Name return } $memberNames = @() if ($Members) { $memberNames = $Members | Resolve-MemberName } $currentMemberNames = (Get-TargetResource -Name $Name).Members $membershipChanges = Resolve-MembershipChange -EnsureMembers $EnsureMembers ` -MemberNames $memberNames ` -CurrentMemberNames $currentMemberNames $membersToInstall = @($currentMemberNames;$membershipChanges.ToAdd) $group = Install-CGroup -Name $Name -Description $Description -Member $membersToInstall -PassThru if( -not $group ) { return } try { $membersToRemove = $group.Members | Where-Object { $memberName = Resolve-PrincipalName -Principal $_ return $membershipChanges.ToRemove -contains $memberName } if( $membersToRemove ) { foreach( $memberToRemove in $membersToRemove ) { Write-Verbose -Message ('[{0}] Members {1} ->' -f $Name,(Resolve-PrincipalName -Principal $memberToRemove)) $group.Members.Remove( $memberToRemove ) } if( $PSCmdlet.ShouldProcess( ('local group {0}' -f $Name), 'remove members' ) ) { $group.Save() } } } finally { $group.Dispose() } } function Test-TargetResource { [CmdletBinding()] [OutputType([bool])] param ( [parameter(Mandatory = $true)] [String] $Name, [String] $Description = $null, [ValidateSet("Present","Absent")] [String] $Ensure = "Present", [ValidateSet('Exact','Present','Absent')] [String] $EnsureMembers = 'Exact', [string[]] $Members = @() ) Set-StrictMode -Version 'Latest' $resource = Get-TargetResource -Name $Name # Do we need to delete the group? if( $Ensure -eq 'Absent' -and $resource.Ensure -eq 'Present' ) { Write-Verbose -Message ('[{0}] Group is present but should be absent.' -f $Name) return $false } # Is it already gone? if( $Ensure -eq 'Absent' -and $resource.Ensure -eq 'Absent' ) { return $true } # Do we need to create the group? if( $Ensure -eq 'Present' -and $resource.Ensure -eq 'Absent' ) { Write-Verbose -Message ('[{0}] Group is absent but should be present.' -f $Name) return $false } # Is the group out-of-date? $upToDate = $true if( $Description -ne $resource.Description ) { Write-Verbose -Message ('[{0}] [Description] ''{1}'' != ''{2}''' -f $Name,$Description,$resource.Description) $upToDate = $false } $memberNames = @() if ($Members) { $memberNames = $Members | Resolve-MemberName } $membershipChanges = Resolve-MembershipChange -EnsureMembers $EnsureMembers ` -MemberNames $memberNames ` -CurrentMemberNames $resource.Members if ($membershipChanges.ToAdd) { $upToDate = $false foreach ($memberName in $membershipChanges.ToAdd) { Write-Verbose -Message ('[{0}] [Members] {1} is absent but should be present' -f $Name,$memberName) } } if ($membershipChanges.ToRemove) { $upToDate = $false foreach ($memberName in $membershipChanges.ToRemove) { Write-Verbose -Message ('[{0}] [Members] {1} is present but should be absent' -f $Name,$memberName) } } return $upToDate } function Resolve-MembershipChange { [CmdletBinding()] [OutputType([hashtable])] param ( [Parameter(Mandatory)] [ValidateSet('Exact','Present','Absent')] [String] $EnsureMembers, [Parameter(Mandatory)] [AllowEmptyCollection()] [String[]] $MemberNames, [Parameter(Mandatory)] [AllowEmptyCollection()] [String[]] $CurrentMemberNames ) $alreadyMembers = @($MemberNames | Where-Object { $_ -in $CurrentMemberNames }) $extraMembers = @($CurrentMemberNames | Where-Object { $_ -notin $MemberNames }) $newMembers = @($MemberNames | Where-Object { $_ -notin $CurrentMemberNames }) $membersToAdd = $null $membersToRemove = $null if ($EnsureMembers -eq 'Present') { $membersToAdd = $newMembers $membersToRemove = @() } elseif ($EnsureMembers -eq 'Absent') { $membersToAdd = @() $membersToRemove = $alreadyMembers } else { $membersToAdd = $newMembers $membersToRemove = $extraMembers } return @{ ToAdd = $membersToAdd ToRemove = $membersToRemove } } function Resolve-MemberName { param( [Parameter(Mandatory, VAlueFromPipeline=$true)] [String] $Name ) process { Resolve-CIdentityName -Name $Name } } function Resolve-PrincipalName { param( [Parameter(Mandatory, ValueFromPipeline=$true)] $Principal ) process { Resolve-CIdentity -SID $Principal.Sid.Value | Select-Object -ExpandProperty 'FullName' } } Export-ModuleMember -Function *-TargetResource |