DSCResources/xCIMSecurity/xCIMSecurity.psm1

#region helper
#region GetAccessMask
Function GetAccessMask
{
    param 
    (
        [Parameter(Mandatory = $true)]
        [ValidateSet(
            "Enable",
            "MethodExecute", 
            "FullWrite",
            "PartialWrite", 
            "ProviderWrite",
            "RemoteAccess",
            "Subscribe",
            "Publish",
            "ReadSecurity", 
            "WriteSecurity" )]
        [string[]]$Rigth
    )

    [int32]$Mask = 0
    foreach ($item in $Rigth)
    {
        switch ($item)
        {
            "Enable"
            {  
                $Mask += 1
            }
            "MethodExecute"
            {
                $Mask += 2
            }
            "FullWrite"
            {
                $Mask += 4
            }
            "PartialWrite"
            {
                $Mask += 8
            }
            "ProviderWrite"
            {
                $Mask += 16
            }
            "RemoteAccess"
            {
                $Mask += 32
            }
            "Subscribe"
            {
                $Mask += 60
            }
            "Publish"
            {
                $Mask += 128
            }
            "ReadSecurity"
            {
                $Mask += 131072
            }
            "WriteSecurity"
            {
                $Mask += 262144
            }
        }
    }   

    $Mask
}
#endregion
#region GetCimSecurity
Function GetCimSecurity
{
    param
    (
        $NameSpace
    )

    $Security = Get-CimInstance -Namespace $NameSpace -ClassName "__SystemSecurity"
    $Descriptor = (Invoke-CimMethod -InputObject $Security -MethodName GetSecurityDescriptor).Descriptor
    $DACL = $Descriptor.DACL

    $has = @{}
    $has.Add("Security", $Security)
    $has.Add("Descriptor", $Descriptor)
    $has.Add("DACL", $DACL)
    $re = New-Object psobject -Property $has
    $re
}
#endregion
#region GetCustomAllDACL
Function GetCustomAllDACL
{
    param
    (
        [ciminstance[]]$Input_CimDACL
    )
    $ALL_DACL = @()
    foreach ($item in $DACL)
    {
        $HAS = @{}
        $HAS.Add("SID", $item.trustee.sidstring)
        $HAS.Add("Mask", $item.AccessMask)
        $HAS.Add("Allow", $item.AceType)
        $HAS.Add("CIMDACL", $item)
        
         $HAS.Add("AceFlags",$item.AceFlags)
        $re = New-Object psobject -Property $HAS 
        $ALL_DACL += $re 
    }
    $ALL_DACL
}
#endregion
#GetAccessMask -Rigth Enable, RemoteAccess


#region Enum

[System.Flags()]
Enum RightMask
{
    Enable = 1
    MethodExecute = 2 
    FullWrite = 4
    PartialWrite = 8
    ProviderWrite = 16
    RemoteAccess = 32
    Subscribe = 60
    Publish = 128
    ReadSecurity = 131072
    WriteSecurity = 262144
}


[System.Flags()]
Enum AceFlags
{
    NameSpaceOnly = 0
    ChildOnly = 10
    WithChild = 2
}
#[RightMask][System.Enum]::Parse([type][AccessRight],13)

#endregion


#region NewACE

Function NewACE
{
    param
    (
        [string]$UserSID,
        [bool]$Allow,
        [AceFlags]$AceFlags,
        [RightMask]$Right
    )
    $trustee = New-CimInstance -Namespace root/cimv2 -ClassName Win32_Trustee -ClientOnly -Property `
        @{
        SidString = $UserSID
    }
    $aceparam = @{}
    $aceparam.Add("Trustee", $trustee)
    $aceparam.Add("AccessMask",  [uint32]([int]$Right))
    $aceparam.Add("AceFlags", [uint32]$AceFlags)
    if ($Allow)
    {
        $aceparam.Add("AceType", [uint32]0)
            
    }
    else
    {
        $aceparam.Add("AceType", [uint32]1)
    }
    $ace = New-CimInstance -Namespace root/cimv2 -ClassName Win32_Ace  -ClientOnly -Property $aceparam

    $ace
}
#endregion
#endregion





#region get
function Get-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]
        $NameSpace,

        [parameter(Mandatory = $true)]
        [System.String]
        $AccountName,


        [parameter(Mandatory = $true)]
        [ciminstance[]]$ACL
    )

    #Write-Verbose "Use this cmdlet to deliver information about command processing."

    #Write-Debug "Use this cmdlet to write debug information while troubleshooting."


    <#
    $returnValue = @{
    NameSpace = [System.String]
    AccountName = [System.String]
    Enusre = [System.String]
    }
 
    $returnValue
    #>

}
#endregion
#region Set
function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]
        $NameSpace,

        [parameter(Mandatory = $true)]
        [System.String]
        $AccountName,
        [parameter(Mandatory = $true)]
        [ciminstance[]]$ACL
    )

    #Write-Verbose "Use this cmdlet to deliver information about command processing."

    #Write-Debug "Use this cmdlet to write debug information while troubleshooting."
     
    #Include this line if the resource requires a system reboot.
    #$global:DSCMachineStatus = 1

    $ALL_DACL = @()
    $NTAccount = New-Object "System.Security.Principal.NTAccount"  $AccountName 
    $UserSID = $NTAccount.Translate([System.Security.Principal.SecurityIdentifier]).Value
    $CimSecurity = (GetCimSecurity -NameSpace $NameSpace)

    $DACL = $CimSecurity.DACL
    $Security = $CimSecurity.Security
    $Descriptor = $CimSecurity.Descriptor
    $ALL_DACL = GetCustomAllDACL -Input_CimDACL $DACL ## all in system
    $Fileter_Result = @()


     $ALL_DACL.foreach(
            {
                if (!($_.Sid -eq $UserSID))
                {
                    $Fileter_Result += $_.CIMDACL
                }
            }
        )
    foreach ($item in $ACL)
    {

        $Fileter_Result += NewACE -UserSID $UserSID  -Allow $item.Allow -AceFlags $item.AceFlags -Right $item.Right
        Write-Verbose ("add" +  $($UserSID) + $( $item.Right)  )
    }

    
        $Descriptor.DACL = $Fileter_Result
    
    Write-Verbose ($Fileter_Result.count)
        Invoke-CimMethod -InputObject $Security -MethodName SetSecurityDescriptor -Arguments @{ Descriptor = $Descriptor}
}
#endregion
#region test
function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]
        $NameSpace,

        [parameter(Mandatory = $true)]
        [System.String]
        $AccountName,

        [parameter(Mandatory = $true)]
        [ciminstance[]]$ACL
    )
    $re = $true
    $ALL_DACL = @()
    $NTAccount = New-Object "System.Security.Principal.NTAccount"  $AccountName 
    $UserSID = $NTAccount.Translate([System.Security.Principal.SecurityIdentifier]).Value
    $DACL = (GetCimSecurity -NameSpace $NameSpace).DACL
    $ALL_DACL = GetCustomAllDACL -Input_CimDACL $DACL ## all in system
    $Fileter_Result = @()
    foreach ($item in $ACL)
    {
        $allowFilter = 0
        if (!$item.Allow)
        {
            $allowFilter = 1 
        }
        $ALL_DACL.foreach(
            {
                if (($_.Sid -eq $UserSID) -and ( $_.Allow -eq $allowFilter)  -and  ($_.AceFlags -eq [int32][AceFlags]$item.AceFlags))
                {
                    $Fileter_Result += $_
                    Write-Verbose ("Find ACL in System $($_.sid) Allow $($item.Allow)")
                }
            }
        )
        if ($Fileter_Result.count -ne 0)
        {
            $Sys_summask = ($Fileter_Result| Measure-Object -Property Mask -Sum).sum 
            $Input_summask = [int32]([RightMask]$item.Right)
            Write-Verbose ("Sys Mask $($Sys_summask) Input Mask $($Input_summask)")

            if ($Sys_summask -ne $Input_summask)
            {
                $re = $false
            }
            $Fileter_Result.Clear()
        }
        else
        {
            $re = $false
        }
    }
    Write-Verbose ("Test Result $($re)")
    $re
}
#endregion


Export-ModuleMember -Function *-TargetResource