DSCResources/MSFT_SPServiceAppSecurity/MSFT_SPServiceAppSecurity.psm1
$script:SPDscUtilModulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\SharePointDsc.Util' Import-Module -Name $script:SPDscUtilModulePath function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] [System.String] $ServiceAppName, [Parameter(Mandatory = $true)] [ValidateSet("Administrators", "SharingPermissions")] [System.String] $SecurityType, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $Members, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $MembersToInclude, [Parameter()] [System.String[]] $MembersToExclude, [Parameter()] [System.Management.Automation.PSCredential] $InstallAccount ) Write-Verbose -Message "Getting all security options for $SecurityType in $ServiceAppName" if ($Members -and (($MembersToInclude) -or ($MembersToExclude))) { $message = ("Cannot use the Members parameter together with the MembersToInclude or " + ` "MembersToExclude parameters") Add-SPDscEvent -Message $message ` -EntryType 'Error' ` -EventID 100 ` -Source $MyInvocation.MyCommand.Source throw $message } if ($null -eq $Members -and $null -eq $MembersToInclude -and $null -eq $MembersToExclude) { $message = ("At least one of the following parameters must be specified: Members, " + ` "MembersToInclude, MembersToExclude") Add-SPDscEvent -Message $message ` -EntryType 'Error' ` -EventID 100 ` -Source $MyInvocation.MyCommand.Source throw $message } $result = Invoke-SPDscCommand -Credential $InstallAccount ` -Arguments $PSBoundParameters ` -ScriptBlock { $params = $args[0] Write-Verbose -Message "Getting Service Application $($params.ServiceAppName)" $serviceApp = Get-SPServiceApplication | Where-Object -FilterScript { $_.Name -eq $params.ServiceAppName } $nullReturn = @{ ServiceAppName = "" SecurityType = $params.SecurityType } if ($null -eq $serviceApp) { return $nullReturn } Write-Verbose -Message "Checking if valid AccessLevels are used" Write-Verbose -Message "Retrieving all available localized AccessLevels" $availablePerms = New-Object System.Collections.ArrayList $appSecurity = Get-SPServiceApplicationSecurity -Identity $serviceApp foreach ($right in $appSecurity.NamedAccessRights) { if (-not $availablePerms.Contains($right.Name)) { $availablePerms.Add($right.Name) | Out-Null } } $appSecurity = Get-SPServiceApplicationSecurity -Identity $serviceApp -Admin foreach ($right in $appSecurity.NamedAccessRights) { if (-not $availablePerms.Contains($right.Name)) { $availablePerms.Add($right.Name) | Out-Null } } if ($params.ContainsKey("Members") -eq $true) { Write-Verbose -Message "Checking AccessLevels in Members parameter" foreach ($member in $params.Members) { foreach ($accessLevel in $member.AccessLevels) { if ($availablePerms -notcontains $accessLevel) { Write-Verbose -Message ("Unknown AccessLevel is used ($accessLevel). " + ` "Allowed values are '" + ` ($availablePerms -join "', '") + "'") return $nullReturn } } } } if ($params.ContainsKey("MembersToInclude") -eq $true) { Write-Verbose -Message "Checking AccessLevels in MembersToInclude parameter" foreach ($member in $params.MembersToInclude) { foreach ($accessLevel in $member.AccessLevels) { if ($availablePerms -notcontains $accessLevel) { Write-Verbose -Message ("Unknown AccessLevel is used ($accessLevel). " + ` "Allowed values are '" + ` ($availablePerms -join "', '") + "'") return $nullReturn } } } } if ($params.ContainsKey("MembersToExclude") -eq $true) { Write-Verbose -Message "Checking AccessLevels in MembersToExclude parameter" foreach ($member in $params.MembersToExclude) { foreach ($accessLevel in $member.AccessLevels) { if ($availablePerms -notcontains $accessLevel) { Write-Verbose -Message ("Unknown AccessLevel is used ($accessLevel). " + ` "Allowed values are '" + ` ($availablePerms -join "', '") + "'") return $nullReturn } } } } switch ($params.SecurityType) { "Administrators" { $security = $serviceApp | Get-SPServiceApplicationSecurity -Admin } "SharingPermissions" { $security = $serviceApp | Get-SPServiceApplicationSecurity } } $members = @() foreach ($securityEntry in $security.AccessRules) { $user = $securityEntry.Name if ($user -like "i:*|*" -or $user -like "c:*|*") { if ($user.Chars(3) -eq "%" -and $user -ilike "*$((Get-SPFarm).Id.ToString())") { $user = "{LocalFarm}" } else { $user = (New-SPClaimsPrincipal -Identity $user -IdentityType EncodedClaim).Value if ($user -match "^s-1-[0-59]-\d+-\d+-\d+-\d+-\d+") { $user = Resolve-SPDscSecurityIdentifier -SID $user } } } $accessLevels = @() foreach ($namedAccessRight in $security.NamedAccessRights) { if ($namedAccessRight.Rights.IsSubsetOf($securityEntry.AllowedObjectRights)) { $accessLevels += $namedAccessRight.Name } } $members += @{ Username = $user AccessLevels = $accessLevels } } return @{ ServiceAppName = $params.ServiceAppName SecurityType = $params.SecurityType Members = $members MembersToInclude = $params.MembersToInclude MembersToExclude = $params.MembersToExclude } } return $result } function Set-TargetResource { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.String] $ServiceAppName, [Parameter(Mandatory = $true)] [ValidateSet("Administrators", "SharingPermissions")] [System.String] $SecurityType, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $Members, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $MembersToInclude, [Parameter()] [System.String[]] $MembersToExclude, [Parameter()] [System.Management.Automation.PSCredential] $InstallAccount ) Write-Verbose -Message "Setting all security options for $SecurityType in $ServiceAppName" if ($Members -and (($MembersToInclude) -or ($MembersToExclude))) { $message = ("Cannot use the Members parameter together with the MembersToInclude or " + ` "MembersToExclude parameters") Add-SPDscEvent -Message $message ` -EntryType 'Error' ` -EventID 100 ` -Source $MyInvocation.MyCommand.Source throw $message } if ($null -eq $Members -and $null -eq $MembersToInclude -and $null -eq $MembersToExclude) { $message = ("At least one of the following parameters must be specified: Members, " + ` "MembersToInclude, MembersToExclude") Add-SPDscEvent -Message $message ` -EntryType 'Error' ` -EventID 100 ` -Source $MyInvocation.MyCommand.Source throw $message } $CurrentValues = Get-TargetResource @PSBoundParameters Invoke-SPDscCommand -Credential $InstallAccount ` -Arguments @($PSBoundParameters, $MyInvocation.MyCommand.Source, $CurrentValues) ` -ScriptBlock { $params = $args[0] $eventSource = $args[1] $CurrentValues = $args[2] $serviceApp = Get-SPServiceApplication | Where-Object -FilterScript { $_.Name -eq $params.ServiceAppName } if ($null -eq $serviceApp) { $message = "Unable to locate service application $($params.ServiceAppName)" Add-SPDscEvent -Message $message ` -EntryType 'Error' ` -EventID 100 ` -Source $eventSource throw $message } Write-Verbose -Message "Checking if valid AccessLevels are used" Write-Verbose -Message "Retrieving all available localized AccessLevels" $availablePerms = New-Object System.Collections.ArrayList $appSecurity = Get-SPServiceApplicationSecurity -Identity $serviceApp foreach ($right in $appSecurity.NamedAccessRights) { if (-not $availablePerms.Contains($right.Name)) { $availablePerms.Add($right.Name) | Out-Null } } $appSecurity = Get-SPServiceApplicationSecurity -Identity $serviceApp -Admin foreach ($right in $appSecurity.NamedAccessRights) { if (-not $availablePerms.Contains($right.Name)) { $availablePerms.Add($right.Name) | Out-Null } } if ($params.ContainsKey("Members") -eq $true) { Write-Verbose -Message "Checking AccessLevels in Members parameter" foreach ($member in $params.Members) { foreach ($accessLevel in $member.AccessLevels) { if ($availablePerms -notcontains $accessLevel) { $message = ("Unknown AccessLevel is used ($accessLevel). Allowed values are " + ` "'" + ($availablePerms -join "', '") + "'") Add-SPDscEvent -Message $message ` -EntryType 'Error' ` -EventID 100 ` -Source $eventSource throw $message } } } } if ($params.ContainsKey("MembersToInclude") -eq $true) { Write-Verbose -Message "Checking AccessLevels in MembersToInclude parameter" foreach ($member in $params.MembersToInclude) { foreach ($accessLevel in $member.AccessLevels) { if ($availablePerms -notcontains $accessLevel) { $message = ("Unknown AccessLevel is used ($accessLevel). Allowed values are " + ` "'" + ($availablePerms -join "', '") + "'") Add-SPDscEvent -Message $message ` -EntryType 'Error' ` -EventID 100 ` -Source $eventSource throw $message } } } } switch ($params.SecurityType) { "Administrators" { $security = $serviceApp | Get-SPServiceApplicationSecurity -Admin } "SharingPermissions" { $security = $serviceApp | Get-SPServiceApplicationSecurity } } $localFarmEncodedClaim = "c:0%.c|system|$((Get-SPFarm).Id.ToString())" if ($params.ContainsKey("Members") -eq $true) { foreach ($desiredMember in $params.Members) { if ($desiredMember.Username -eq "{LocalFarm}") { $claim = New-SPClaimsPrincipal -Identity $localFarmEncodedClaim ` -IdentityType EncodedClaim } else { $isUser = Test-SPDscIsADUser -IdentityName $desiredMember.Username if ($isUser -eq $true) { $claim = New-SPClaimsPrincipal -Identity $desiredMember.Username ` -IdentityType WindowsSamAccountName } else { $claim = New-SPClaimsPrincipal -Identity $desiredMember.Username ` -IdentityType WindowsSecurityGroupName } } if ($CurrentValues.Members.Username -contains $desiredMember.Username) { $compare = Compare-Object -ReferenceObject ($CurrentValues.Members | Where-Object -FilterScript { $_.Username -eq $desiredMember.Username } | Select-Object -First 1).AccessLevels -DifferenceObject $desiredMember.AccessLevels if ($null -ne $compare) { Grant-SPObjectSecurity -Identity $security ` -Principal $claim ` -Rights $desiredMember.AccessLevels ` -Replace } } else { Grant-SPObjectSecurity -Identity $security -Principal $claim -Rights $desiredMember.AccessLevels } } foreach ($currentMember in $CurrentValues.Members) { if ($params.Members.Username -notcontains $currentMember.Username) { if ($currentMember.UserName -eq "{LocalFarm}") { $claim = New-SPClaimsPrincipal -Identity $localFarmEncodedClaim ` -IdentityType EncodedClaim } else { $isUser = Test-SPDscIsADUser -IdentityName $currentMember.Username if ($isUser -eq $true) { $claim = New-SPClaimsPrincipal -Identity $currentMember.Username ` -IdentityType WindowsSamAccountName } else { $claim = New-SPClaimsPrincipal -Identity $currentMember.Username ` -IdentityType WindowsSecurityGroupName } } Revoke-SPObjectSecurity -Identity $security -Principal $claim } } } if ($params.ContainsKey("MembersToInclude") -eq $true) { foreach ($desiredMember in $params.MembersToInclude) { if ($desiredMember.Username -eq "{LocalFarm}") { $claim = New-SPClaimsPrincipal -Identity $localFarmEncodedClaim ` -IdentityType EncodedClaim } else { $isUser = Test-SPDscIsADUser -IdentityName $desiredMember.Username if ($isUser -eq $true) { $claim = New-SPClaimsPrincipal -Identity $desiredMember.Username ` -IdentityType WindowsSamAccountName } else { $claim = New-SPClaimsPrincipal -Identity $desiredMember.Username ` -IdentityType WindowsSecurityGroupName } } if ($CurrentValues.Members.Username -contains $desiredMember.Username) { $compare = Compare-Object -ReferenceObject ($CurrentValues.Members | Where-Object -FilterScript { $_.Username -eq $desiredMember.Username } | Select-Object -First 1).AccessLevels -DifferenceObject $desiredMember.AccessLevels if ($null -ne $compare) { Grant-SPObjectSecurity -Identity $security ` -Principal $claim ` -Rights $desiredMember.AccessLevels ` -Replace } } else { Grant-SPObjectSecurity -Identity $security ` -Principal $claim ` -Rights $desiredMember.AccessLevels } } } if ($params.ContainsKey("MembersToExclude") -eq $true) { foreach ($excludeMember in $params.MembersToExclude) { if ($CurrentValues.Members.Username -contains $excludeMember) { if ($excludeMember -eq "{LocalFarm}") { $claim = New-SPClaimsPrincipal -Identity $localFarmEncodedClaim ` -IdentityType EncodedClaim } else { $isUser = Test-SPDscIsADUser -IdentityName $excludeMember if ($isUser -eq $true) { $claim = New-SPClaimsPrincipal -Identity $excludeMember ` -IdentityType WindowsSamAccountName } else { $claim = New-SPClaimsPrincipal -Identity $excludeMember ` -IdentityType WindowsSecurityGroupName } } Revoke-SPObjectSecurity -Identity $security -Principal $claim } } } switch ($params.SecurityType) { "Administrators" { $security = $serviceApp | Set-SPServiceApplicationSecurity -ObjectSecurity $security ` -Admin } "SharingPermissions" { $security = $serviceApp | Set-SPServiceApplicationSecurity -ObjectSecurity $security } } } } function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter(Mandatory = $true)] [System.String] $ServiceAppName, [Parameter(Mandatory = $true)] [ValidateSet("Administrators", "SharingPermissions")] [System.String] $SecurityType, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $Members, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $MembersToInclude, [Parameter()] [System.String[]] $MembersToExclude, [Parameter()] [System.Management.Automation.PSCredential] $InstallAccount ) Write-Verbose -Message "Testing all security options for $SecurityType in $ServiceAppName" $CurrentValues = Get-TargetResource @PSBoundParameters Write-Verbose -Message "Current Values: $(Convert-SPDscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-SPDscHashtableToString -Hashtable $PSBoundParameters)" if ([System.String]::IsNullOrEmpty($CurrentValues.ServiceAppName) -eq $true) { $message = "ServiceAppName is currently not configured in the environment." Write-Verbose -Message $message Add-SPDscEvent -Message $message -EntryType 'Error' -EventID 1 -Source $MyInvocation.MyCommand.Source Write-Verbose -Message "Test-TargetResource returned false" return $false } $result = Invoke-SPDscCommand -Credential $InstallAccount ` -Arguments @($PSBoundParameters, $CurrentValues, $PSScriptRoot, $MyInvocation.MyCommand.Source) ` -ScriptBlock { $params = $args[0] $CurrentValues = $args[1] $ScriptRoot = $args[2] $Source = $args[3] $relPath = "..\..\Modules\SharePointDsc.ServiceAppSecurity\SPServiceAppSecurity.psm1" Import-Module (Join-Path -Path $ScriptRoot -ChildPath $relPath -Resolve) $serviceApp = Get-SPServiceApplication | Where-Object -FilterScript { $_.Name -eq $params.ServiceAppName } switch ($params.SecurityType) { "Administrators" { $security = $serviceApp | Get-SPServiceApplicationSecurity -Admin } "SharingPermissions" { $security = $serviceApp | Get-SPServiceApplicationSecurity } } if ($null -ne $params.Members) { Write-Verbose -Message "Processing Members parameter" if ($CurrentValues.Members.Count -eq 0) { if ($params.Members.Count -gt 0) { $message = ("Security list does not match. Actual: $($CurrentValues.Members.Username -join ", "). " + ` "Desired: $($params.Members.Username -join ", ")") Write-Verbose -Message $message Add-SPDscEvent -Message $message -EntryType 'Error' -EventID 1 -Source $Source return $false } else { Write-Verbose -Message "Configured and specified security lists are both empty" return $true } } elseif ($params.Members.Count -eq 0) { $message = ("Security list does not match. Actual: $($CurrentValues.Members.Username -join ", "). " + ` "Desired: $($params.Members.Username -join ", ")") Write-Verbose -Message $message Add-SPDscEvent -Message $message -EntryType 'Error' -EventID 1 -Source $Source return $false } $differences = Compare-Object -ReferenceObject $CurrentValues.Members.Username ` -DifferenceObject $params.Members.Username if ($null -eq $differences) { Write-Verbose -Message "Security list matches - checking that permissions match on each object" foreach ($currentMember in $CurrentValues.Members) { $expandedAccessLevels = Expand-AccessLevel -Security $security -AccessLevels ($params.Members | Where-Object -FilterScript { $_.Username -eq $currentMember.Username } | Select-Object -First 1).AccessLevels if ($null -ne (Compare-Object -DifferenceObject $currentMember.AccessLevels -ReferenceObject $expandedAccessLevels)) { $message = "$($currentMember.Username) has incorrect permission level. Test failed." Write-Verbose -Message $message Add-SPDscEvent -Message $message -EntryType 'Error' -EventID 1 -Source $Source return $false } } return $true } else { $message = ("Security list does not match. Actual: $($CurrentValues.Members.Username -join ", "). " + ` "Desired: $($params.Members.Username -join ", ")") Write-Verbose -Message $message Add-SPDscEvent -Message $message -EntryType 'Error' -EventID 1 -Source $Source return $false } } $result = $true if ($params.MembersToInclude) { Write-Verbose -Message "Processing MembersToInclude parameter" foreach ($member in $params.MembersToInclude) { if (-not($CurrentValues.Members.Username -contains $member.Username)) { $message = "$($member.Username) does not have access. Test failed." Write-Verbose -Message $message Add-SPDscEvent -Message $message -EntryType 'Error' -EventID 1 -Source $Source $result = $false } else { Write-Verbose -Message "$($member.Username) already has access. Checking permission..." $expandedAccessLevels = Expand-AccessLevel -Security $security -AccessLevels $member.AccessLevels $compare = Compare-Object -DifferenceObject $expandedAccessLevels -ReferenceObject ($CurrentValues.Members | Where-Object -FilterScript { $_.Username -eq $member.Username } | Select-Object -First 1).AccessLevels if ($null -ne $compare) { $message = "$($member.Username) has incorrect permission level. Test failed." Write-Verbose -Message $message Add-SPDscEvent -Message $message -EntryType 'Error' -EventID 1 -Source $Source return $false } } } } if ($params.MembersToExclude) { Write-Verbose -Message "Processing MembersToExclude parameter" foreach ($member in $params.MembersToExclude) { if ($CurrentValues.Members.Username -contains $member) { $message = "$member already has access. Set result to false" Write-Verbose -Message $message Add-SPDscEvent -Message $message -EntryType 'Error' -EventID 1 -Source $Source $result = $false } else { Write-Verbose -Message "$member does not have access. Skipping" } } } return $result } Write-Verbose -Message "Test-TargetResource returned $result" return $result } function Export-TargetResource { $VerbosePreference = "SilentlyContinue" $ParentModuleBase = Get-Module "SharePointDsc" -ListAvailable | Select-Object -ExpandProperty Modulebase $module = Join-Path -Path $ParentModuleBase -ChildPath "\DSCResources\MSFT_SPServiceAppSecurity\MSFT_SPServiceAppSecurity.psm1" -Resolve $Content = '' $params = Get-DSCFakeParameters -ModulePath $module $serviceApplications = Get-SPServiceApplication | Where-Object { $_.GetType().Name -ne "SPUsageApplication" -and $_.GetType().Name -ne "StateServiceApplication" } foreach ($serviceApp in $serviceApplications) { try { $params.ServiceAppName = $serviceApp.Name $params.SecurityType = "SharingPermissions" $property = @{ Handle = 0 } $fake = New-CimInstance -ClassName Win32_Process -Property $property -Key Handle -ClientOnly $params.Members = $fake $params.Remove("MembersToInclude") $params.Remove("MembersToExclude") $results = Get-TargetResource @params $results = Repair-Credentials -results $results $results.Remove("MembersToInclude") $results.Remove("MembersToExclude") if ($results.Members.Count -gt 0) { $PartialContent = '' $stringMember = "" $foundOne = $false foreach ($member in $results.Members) { $stringMember = Get-SPDscServiceAppSecurityMembers $member if ($null -ne $stringMember) { if (!$foundOne) { $PartialContent += " `$members = @();`r`n" $foundOne = $true } $PartialContent += " `$members += " + $stringMember + ";`r`n" } } if ($foundOne) { $PartialContent += " SPServiceAppSecurity " + [System.Guid]::NewGuid().ToString() + "`r`n" $PartialContent += " {`r`n" $results.Members = "`$members" $currentBlock = Get-DSCBlock -Params $results -ModulePath $module $currentBlock = Convert-DSCStringParamToVariable -DSCBlock $currentBlock -ParameterName "PsDscRunAsCredential" $PartialContent += $currentBlock $PartialContent += " }`r`n" $Content += $PartialContent } } $params.SecurityType = "Administrators" $results = Get-TargetResource @params $results = Repair-Credentials -results $results $results.Remove("MembersToInclude") $results.Remove("MembersToExclude") $stringMember = "" if ($results.Members.Count -gt 0) { $PartialContent = '' $foundOne = $false foreach ($member in $results.Members) { $stringMember = Get-SPDscServiceAppSecurityMembers $member if ($null -ne $stringMember) { if (!$foundOne) { $PartialContent += " `$members = @();`r`n" $foundOne = $true } $PartialContent += " `$members += " + $stringMember + ";`r`n" } } if ($foundOne) { $PartialContent += " SPServiceAppSecurity " + [System.Guid]::NewGuid().ToString() + "`r`n" $PartialContent += " {`r`n" $results.Members = "`$members" $currentBlock = Get-DSCBlock -Params $results -ModulePath $module $currentBlock = Convert-DSCStringParamToVariable -DSCBlock $currentBlock -ParameterName "PsDscRunAsCredential" $PartialContent += $currentBlock $PartialContent += " }`r`n" $Content += $PartialContent } } } catch { $_ $Global:ErrorLog += "[Service Application Permissions]" + $serviceApp.Name + "`r`n" $Global:ErrorLog += "$_`r`n`r`n" } } return $Content.ToString() } Export-ModuleMember -Function *-TargetResource |