Resources/SecurityPolicy.psm1

# import helper script modules
Import-Module $(Join-Path -Path $PSScriptRoot -ChildPath 'DscBaseline.helper.psm1')
Import-Module $(Join-Path -Path $PSScriptRoot -ChildPath 'SecurityPolicy.helper.psm1')

function Invoke-AccountPolicy
{
    <#
      .SYNOPSIS
        Creates Account Policy DSC configuration
    #>


    [OutputType([System.String])]
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory=$true)]
        [ValidateScript({$_.Count -gt 1})]
        [System.Collections.Hashtable]
        # Security Policy configuration hashtable.
        $SecPolObj
        ,
        [parameter(Mandatory=$false)]
        [ValidateScript({Test-Path -Path $_ -PathType Container})]
        [String]
        # Folder to save Account Policy DSC config.
        $Folder = (Get-Location).Path
    )

    [string[]] $dscConfig = @()

    $destFile = Join-Path -Path $Folder -ChildPath 'DscBaselineAccountPolicy.ps1'

    $dscConfig += 'Configuration DscBaselineAccountPolicy'
    $dscConfig += '{'
    $dscConfig += " # Generated: $(Get-Date) on $($env:COMPUTERNAME)"
    $dscConfig += ' # ref: https://github.com/dsccommunity/SecurityPolicyDsc#accountpolicy'
    $dscConfig += ''
    $dscConfig += ' Import-DscResource -Module SecurityPolicyDsc -Name AccountPolicy'
    $dscConfig += ''
    $dscConfig += ' Node localhost'
    $dscConfig += ' {'
    $dscConfig += ' AccountPolicy SecurityPolicy_AccountPolicy'
    $dscConfig += ' {'
    
    [string] $space = (4..45 | ForEach-Object{ ' ' }) -join ''

    $dscConfig += @("$($global:CONFIG_INDENT) Name$($space)= `'SecurityPolicy_AccountPolicy`'")

    $accountPolicyData = Get-SecurityPolicyData -Section AccountPolicy

    [string[]] $keyList = $accountPolicyData.Keys | Sort-Object

    foreach($key in $keyList)
    {
        $section = $accountPolicyData[$key].Section

        if($SecPolObj.ContainsKey($section))
        {
            if($SecPolObj[$section].ContainsKey($accountPolicyData[$key].Value))
            {
                $value = ''

                if($key -eq 'Maximum_Password_Age')
                {
                    # Maximum_Password_Age could be -1 which needs to be 0 for DSC config.
                    [int] $maxPassAgeValue = $SecPolObj[$section][$($accountPolicyData[$key].Value)]

                    if($maxPassAgeValue -lt 0)
                    {
                        $value = '0'
                    
                    } else {

                        $value = $maxPassAgeValue
                    }
                
                } else {
                
                    if($accountPolicyData[$key].Option.ContainsKey('String'))
                    {
                        $value = $SecPolObj[$section][$($accountPolicyData[$key].Value)]

                    } else {

                        $reverseHash = ConvertTo-ReverseHashTable $accountPolicyData[$key].Option

                        $value = $reverseHash[$($SecPolObj[$section][$($accountPolicyData[$key].Value)])]
                    }
                }
                
                # format output
                [string] $space = ' '

                if($key.Length -lt 44)
                {
                    $key.Length..44 | ForEach-Object{ $space = $space + ' ' }
                }
    
                if($key.ToLower() -eq 'password_must_meet_complexity_requirements' -or $key.ToLower() -eq 'store_passwords_using_reversible_encryption')
                {
                    $dscConfig += "$($global:CONFIG_INDENT) $($key)$($space)= '$($value)'"

                } else {
                    
                    $dscConfig += "$($global:CONFIG_INDENT) $($key)$($space)= $($value)"
                }
            }
        }
    }

    $dscConfig += ' }'
    $dscConfig += ' }'
    $dscConfig += '}'
    $dscConfig += ''
    $dscConfig += ". DscBaselineAccountPolicy -OutputPath $(Join-Path $Folder -ChildPath 'DscBaselineAccountPolicy') -Verbose"
    $dscConfig += ''

    Out-File -FilePath $destFile -InputObject $dscConfig -Encoding ASCII -Force

    return $destFile

} # end function Invoke-AccountPolicy

function Invoke-SecurityOption
{
    <#
      .SYNOPSIS
        Creates Security Option DSC configuration
    #>


    [OutputType([System.String])]
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory=$true)]
        [ValidateScript({$_.Count -gt 1})]
        [System.Collections.Hashtable]
        # Security Policy configuration object.
        $SecPolObj
        ,
        [parameter(Mandatory=$false)]
        [ValidateScript({Test-Path -Path $_ -PathType Container})]
        [String]
        # Folder to save Security Option DSC config.
        $Folder = (Get-Location).Path
    )

    [string[]] $dscConfig = @()
    
    $destFile = Join-Path -Path $Folder -ChildPath 'DscBaselineSecurityOption.ps1'

    $dscConfig += 'Configuration DscBaselineSecurityOption'
    $dscConfig += '{'
    $dscConfig += " # Generated: $(Get-Date) on $($env:COMPUTERNAME)"
    $dscConfig += ' # ref: https://github.com/dsccommunity/SecurityPolicyDsc#securityoption'
    $dscConfig += ''
    $dscConfig += ' Import-DscResource -Module SecurityPolicyDsc -Name SecurityOption'
    $dscConfig += ''
    $dscConfig += ' Node localhost'
    $dscConfig += ' {'
    $dscConfig += ' SecurityOption SecurityPolicy_SecurityOption'
    $dscConfig += ' {'
    
    [string] $space = (4..100 | ForEach-Object{ ' ' }) -join ''

    $dscConfig += @("$($global:CONFIG_INDENT) Name$($space)= `'SecurityPolicy_SecurityOption`'")

    $securityOptionData = Get-SecurityPolicyData -Section SecurityOption

    [string[]] $keyList = $securityOptionData.Keys | Sort-Object

    foreach($key in $keyList)
    {
        $section = $securityOptionData[$key].Section

        if($SecPolObj.ContainsKey($section))
        {
            if($SecPolObj[$section].ContainsKey($securityOptionData[$key].Value))
            {
                $value = ''
            
                if($securityOptionData[$key].Option.ContainsKey('String'))
                {
                    $value = $SecPolObj[$section][$($securityOptionData[$key].Value)]

                    # get any string prefix
                    $stringValue = $securityOptionData[$key].Option.string

                    # remove any string prefix
                    $value = $value.Substring($stringValue.Length, $($value.Length - $stringValue.Length))

                } else {

                    $reverseHash = ConvertTo-ReverseHashTable $securityOptionData[$key].Option

                    $value = $reverseHash[$($SecPolObj[$section][$($securityOptionData[$key].Value)])]
                }

                if($null -ne $value)
                {
                    if($value.StartsWith('"') -eq $true -and $value.EndsWith('"') -eq $true)
                    {
                        $value = $value.Substring(1,$($value.Length -2))
                    } 
                    
                    $value = "`'$($value)`'"
                
                } else {

                    if($key.ToUpper() -eq 'NETWORK_SECURITY_CONFIGURE_ENCRYPTION_TYPES_ALLOWED_FOR_KERBEROS')
                    {
                        $value = Convertto-QuotesAndCommas -InputObj @('RC4_HMAC_MD5','AES128_HMAC_SHA1','AES256_HMAC_SHA1','FUTURE')
                    }
                }

                # format output
                [string] $space = ' '
                
                if($key.Length -lt 100)
                {
                    $key.Length..99 | ForEach-Object{ $space = $space + ' ' }
                }
    
                if($value -eq "''")
                {
                    # Settings with empty values can produce errors. Keeping config
                    # though commenting it out for future configurations.
                    $dscConfig += "$($global:CONFIG_INDENT) #$($key)$($space)= $($value)"

                } else {

                    $dscConfig += "$($global:CONFIG_INDENT) $($key)$($space)= $($value)"
                }
            }
        }
    }

    $dscConfig += ' }'
    $dscConfig += ' }'
    $dscConfig += '}'
    $dscConfig += ''
    $dscConfig += ". DscBaselineSecurityOption -OutputPath $(Join-Path $Folder -ChildPath 'DscBaselineSecurityOption') -Verbose"
    $dscConfig += ''

    Out-File -FilePath $destFile -InputObject $dscConfig -Encoding ASCII -Force

    return $destFile

} # end function Invoke-SecurityOption

function Invoke-UserRights
{
    <#
      .SYNOPSIS
        Creates User Rights DSC configuration
    #>


    [OutputType([System.String])]
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory=$true)]
        [ValidateScript({$_.Count -gt 1})]
        [System.Collections.Hashtable]
        # Security Policy configuration object.
        $SecPolObj
        ,
        [parameter(Mandatory=$false)]
        [ValidateScript({Test-Path -Path $_ -PathType Container})]
        [String]
        # Folder to save User Rights DSC config.
        $Folder = (Get-Location).Path
    )

    [string[]] $dscConfig = @()
    
    $destFile = Join-Path -Path $Folder -ChildPath 'DscBaselineUserRightsAssignment.ps1'

    $dscConfig += 'Configuration DscBaselineUserRightsAssignment'
    $dscConfig += '{'
    $dscConfig += " # Generated: $(Get-Date) on $($env:COMPUTERNAME)"
    $dscConfig += ' # ref: https://github.com/dsccommunity/SecurityPolicyDsc#userrightsassignment'
    $dscConfig += ''
    $dscConfig += ' Import-DscResource -Module SecurityPolicyDsc -Name UserRightsAssignment'
    $dscConfig += ''
    $dscConfig += ' Node localhost'
    $dscConfig += ' {'

    $userRightsData = Get-SecurityPolicyData -Section UserRights

    [string[]] $keyList = $userRightsData.Keys | Sort-Object

    foreach($key in $keyList)
    {
        if($SecPolObj.ContainsKey('Privilege Rights'))
        {
            if($SecPolObj['Privilege Rights'].ContainsKey($userRightsData[$key]))
            {
                [string[]] $splitChars = ',','*'

                $sids = $SecPolObj['Privilege Rights'][$($userRightsData[$key])]

                [string[]] $sidsArray = $sids.Split($splitChars,[System.StringSplitOptions]::RemoveEmptyEntries)

                [string[]] $friendlyNames = ConvertTo-LocalFriendlyName -Identity $sidsArray

                $identityValue = Convertto-QuotesAndCommas -InputObj $friendlyNames
                $dscName       = Convertto-DscConfigurationName "$($key) UserRightsAssignment"

                $dscConfig += "$($global:CONFIG_INDENT)UserRightsAssignment $($dscName)"
                $dscConfig += "$($global:CONFIG_INDENT){"
                $dscConfig += "$($global:CONFIG_INDENT) Policy = `'$($key)`'"
                $dscConfig += "$($global:CONFIG_INDENT) Identity = $($identityValue)"
                $dscConfig += "$($global:CONFIG_INDENT) Ensure = `'Present`'"
                $dscConfig += "$($global:CONFIG_INDENT)}"
            }
        }
    }

    $dscConfig += ' }'
    $dscConfig += '}'
    $dscConfig += ''
    $dscConfig += ". DscBaselineUserRightsAssignment -OutputPath $(Join-Path $Folder -ChildPath 'DscBaselineUserRightsAssignment') -Verbose"
    $dscConfig += ''

    Out-File -FilePath $destFile -InputObject $dscConfig -Encoding ASCII -Force

    return $destFile

} # end function Invoke-UserRights

function Get-DscBaselineSecurityPolicy
{
    <#
      .SYNOPSIS
        Generates a Security Policy DSC configuration based on the local system.
      .EXAMPLE
        Get-DscBaselineSecurityPolicy
      .EXAMPLE
        Get-DscBaselineSecurityPolicy -FilePath D:\SomeFolder\
    #>

    
    [OutputType([System.Collections.Hashtable])]
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory=$false)]
        [ValidateScript({Test-Path -Path $_ -PathType Container})]
        [String]
        # Folder to save new .ps1 file
        $Folder = (Get-Location).Path
    )

    $dscConfigFiles = @{}

    $securityPolicyResultFile = Invoke-SecEdit

    if($null -ne $securityPolicyResultFile)
    {
        $securityPolicyObject = Convert-SecurityPolicyIniToHash -Path $securityPolicyResultFile

        for($i=1;$i -lt 4; $i++)
        {
            switch($i)
            {
                1 { # ACCOUNTPOLICY
                
                    $accountPolicyDscConfig = Invoke-AccountPolicy -SecPolObj $securityPolicyObject -Folder $Folder

                    $dscConfigFiles.Add('AccountPolicy', $accountPolicyDscConfig)
                }
                2 { # SECURITYOPTION

                    $securityOptionDscConfig = Invoke-SecurityOption -SecPolObj $securityPolicyObject -Folder $Folder

                    $dscConfigFiles.Add('SecurityOption', $securityOptionDscConfig)
                }
                3 { # USERRIGHTS

                    $userRightsDscConfig = Invoke-UserRights -SecPolObj $securityPolicyObject -Folder $Folder

                    $dscConfigFiles.Add('UserRights', $userRightsDscConfig)
                }
            }
        }
    }

    return $dscConfigFiles

} # end function Get-DscBaselineSecurityPolicy

Export-ModuleMember -Function 'Get-DscBaselineSecurityPolicy'