HelperFunctions.psm1

$AuditHash = @{ 
"0cce9210-69ae-11d9-bed3-505054503030" = "Audit_System_SecurityStateChange";
"0cce9211-69ae-11d9-bed3-505054503030" = "Audit_System_SecuritySubsystemExtension";
"0cce9212-69ae-11d9-bed3-505054503030" = "Audit_System_Integrity";
"0cce9213-69ae-11d9-bed3-505054503030" = "Audit_System_IPSecDriverEvents";
"0cce9214-69ae-11d9-bed3-505054503030" = "Audit_System_Others";
"0cce9215-69ae-11d9-bed3-505054503030" = "Audit_Logon_Logon";
"0cce9216-69ae-11d9-bed3-505054503030" = "Audit_Logon_Logoff";
"0cce9217-69ae-11d9-bed3-505054503030" = "Audit_Logon_AccountLockout";
"0cce9218-69ae-11d9-bed3-505054503030" = "Audit_Logon_IPSecMainMode";
"0cce9219-69ae-11d9-bed3-505054503030" = "Audit_Logon_IPSecQuickMode";
"0cce921a-69ae-11d9-bed3-505054503030" = "Audit_Logon_IPSecUserMode";
"0cce921b-69ae-11d9-bed3-505054503030" = "Audit_Logon_SpecialLogon";
"0cce921c-69ae-11d9-bed3-505054503030" = "Audit_Logon_Others";
"0cce921d-69ae-11d9-bed3-505054503030" = "Audit_ObjectAccess_FileSystem";
"0cce921e-69ae-11d9-bed3-505054503030" = "Audit_ObjectAccess_Registry";
"0cce921f-69ae-11d9-bed3-505054503030" = "Audit_ObjectAccess_Kernel";
"0cce9220-69ae-11d9-bed3-505054503030" = "Audit_ObjectAccess_Sam";
"0cce9221-69ae-11d9-bed3-505054503030" = "Audit_ObjectAccess_CertificationServices";
"0cce9222-69ae-11d9-bed3-505054503030" = "Audit_ObjectAccess_ApplicationGenerated";
"0cce9223-69ae-11d9-bed3-505054503030" = "Audit_ObjectAccess_Handle";
"0cce9224-69ae-11d9-bed3-505054503030" = "Audit_ObjectAccess_Share";
"0cce9225-69ae-11d9-bed3-505054503030" = "Audit_ObjectAccess_FirewallPacketDrops";
"0cce9226-69ae-11d9-bed3-505054503030" = "Audit_ObjectAccess_FirewallConnection";
"0cce9227-69ae-11d9-bed3-505054503030" = "Audit_ObjectAccess_Other";
"0cce9228-69ae-11d9-bed3-505054503030" = "Audit_PrivilegeUse_Sensitive";
"0cce9229-69ae-11d9-bed3-505054503030" = "Audit_PrivilegeUse_NonSensitive";
"0cce922a-69ae-11d9-bed3-505054503030" = "Audit_PrivilegeUse_Others";
"0cce922b-69ae-11d9-bed3-505054503030" = "Audit_DetailedTracking_ProcessCreation";
"0cce922c-69ae-11d9-bed3-505054503030" = "Audit_DetailedTracking_ProcessTermination";
"0cce922d-69ae-11d9-bed3-505054503030" = "Audit_DetailedTracking_DpapiActivity";
"0cce922e-69ae-11d9-bed3-505054503030" = "Audit_DetailedTracking_RpcCall";
"0cce922f-69ae-11d9-bed3-505054503030" = "Audit_PolicyChange_AuditPolicy";
"0cce9230-69ae-11d9-bed3-505054503030" = "Audit_PolicyChange_AuthenticationPolicy";
"0cce9231-69ae-11d9-bed3-505054503030" = "Audit_PolicyChange_AuthorizationPolicy";
"0cce9232-69ae-11d9-bed3-505054503030" = "Audit_PolicyChange_MpsscvRulePolicy";
"0cce9233-69ae-11d9-bed3-505054503030" = "Audit_PolicyChange_WfpIPSecPolicy";
"0cce9234-69ae-11d9-bed3-505054503030" = "Audit_PolicyChange_Others";
"0cce9235-69ae-11d9-bed3-505054503030" = "Audit_AccountManagement_UserAccount";
"0cce9236-69ae-11d9-bed3-505054503030" = "Audit_AccountManagement_ComputerAccount";
"0cce9237-69ae-11d9-bed3-505054503030" = "Audit_AccountManagement_SecurityGroup";
"0cce9238-69ae-11d9-bed3-505054503030" = "Audit_AccountManagement_DistributionGroup";
"0cce9239-69ae-11d9-bed3-505054503030" = "Audit_AccountManagement_ApplicationGroup";
"0cce923a-69ae-11d9-bed3-505054503030" = "Audit_AccountManagement_Others";
"0cce923b-69ae-11d9-bed3-505054503030" = "Audit_DSAccess_DSAccess";
"0cce923c-69ae-11d9-bed3-505054503030" = "Audit_DsAccess_AdAuditChanges";
"0cce923d-69ae-11d9-bed3-505054503030" = "Audit_Ds_Replication";
"0CCE9244-69AE-11D9-BED3-505054503030" = "Detailed File Share";
"0cce923e-69ae-11d9-bed3-505054503030" = "Audit_Ds_DetailedReplication";
"0cce923f-69ae-11d9-bed3-505054503030" = "Audit_AccountLogon_CredentialValidation";
"0cce9240-69ae-11d9-bed3-505054503030" = "Audit_AccountLogon_Kerberos";
"0cce9241-69ae-11d9-bed3-505054503030" = "Audit_AccountLogon_Others";
"0cce9242-69ae-11d9-bed3-505054503030" = "Audit_AccountLogon_KerbCredentialValidation";
"0cce9243-69ae-11d9-bed3-505054503030" = "Audit_Logon_NPS";
}

$AuditCategoryHash = @{
"0CCE9211-69AE-11D9-BED3-505054503030" = "Security System Extension";
"0CCE9212-69AE-11D9-BED3-505054503030" = "System Integrity";
"0CCE9213-69AE-11D9-BED3-505054503030" = "IPsec Driver";
"0CCE9214-69AE-11D9-BED3-505054503030" = "Other System Events";
"0CCE9210-69AE-11D9-BED3-505054503030" = "Security State Change";
"0CCE9215-69AE-11D9-BED3-505054503030" = "Logon";
"0CCE9216-69AE-11D9-BED3-505054503030" = "Logoff";
"0CCE9217-69AE-11D9-BED3-505054503030" = "Account Lockout"
"0CCE9218-69AE-11D9-BED3-505054503030" = "IPsec Main Mode";
"0CCE9219-69AE-11D9-BED3-505054503030" = "IPsec Quick Mode";
"0CCE921A-69AE-11D9-BED3-505054503030" = "IPsec Extended Mode";
"0CCE921B-69AE-11D9-BED3-505054503030" = "Special Logon";
"0CCE921C-69AE-11D9-BED3-505054503030" = "Other Logon/Logoff Events";
"0CCE9243-69AE-11D9-BED3-505054503030" = "Network Policy Server";
"0cce9247-69ae-11d9-bed3-505054503030" = "User / Device Claims";
"0cce9249-69ae-11d9-bed3-505054503030" = "Group Membership";
"0CCE921D-69AE-11D9-BED3-505054503030" = "File System";
"0CCE921E-69AE-11D9-BED3-505054503030" = "Registry";
"0CCE921F-69AE-11D9-BED3-505054503030" = "Kernel Object";
"0CCE9220-69AE-11D9-BED3-505054503030" = "SAM";
"0CCE9221-69AE-11D9-BED3-505054503030" = "Certification Services";
"0CCE9222-69AE-11D9-BED3-505054503030" = "Application Generated";
"0CCE9223-69AE-11D9-BED3-505054503030" = "Handle Manipulation";
"0CCE9224-69AE-11D9-BED3-505054503030" = "File Share";
"0CCE9225-69AE-11D9-BED3-505054503030" = "Filtering Platform Packet Drop";
"0CCE9226-69AE-11D9-BED3-505054503030" = "Filtering Platform Connection";
"0CCE9227-69AE-11D9-BED3-505054503030" = "Other Object Access Events";
"0CCE9244-69AE-11D9-BED3-505054503030" = "Detailed File Share";
"0CCE9245-69AE-11D9-BED3-505054503030" = "Removable Storage";
"0CCE9246-69AE-11D9-BED3-505054503030" = "Central Policy Staging";
"0CCE9229-69AE-11D9-BED3-505054503030" = "Non Sensitive Privilege Use";
"0CCE922A-69AE-11D9-BED3-505054503030" = "Other Privilege Use Events";
"0CCE9228-69AE-11D9-BED3-505054503030" = "Sensitive Privilege Use";
"0CCE922B-69AE-11D9-BED3-505054503030" = "Process Creation";
"0CCE922C-69AE-11D9-BED3-505054503030" = "Process Termination";
"0CCE922D-69AE-11D9-BED3-505054503030" = "DPAPI Activity";
"0CCE922E-69AE-11D9-BED3-505054503030" = "RPC Events";
"0cce9248-69ae-11d9-bed3-505054503030" = "Plug and Play Events";
"0CCE9230-69AE-11D9-BED3-505054503030" = "Authentication Policy Change";
"0CCE9231-69AE-11D9-BED3-505054503030" = "Authorization Policy Change";
"0CCE9232-69AE-11D9-BED3-505054503030" = "MPSSVC Rule-Level Policy Change";
"0CCE9233-69AE-11D9-BED3-505054503030" = "Filtering Platform Policy Change";
"0CCE9234-69AE-11D9-BED3-505054503030" = "Other Policy Change Events";
"0CCE922F-69AE-11D9-BED3-505054503030" = "Audit Policy Change";
"0CCE9235-69AE-11D9-BED3-505054503030" = "User Account Management";
"0CCE9236-69AE-11D9-BED3-505054503030" = "Computer Account Management";
"0CCE9237-69AE-11D9-BED3-505054503030" = "Security Group Management";
"0CCE9238-69AE-11D9-BED3-505054503030" = "Distribution Group Management";
"0CCE9239-69AE-11D9-BED3-505054503030" = "Application Group Management";
"0CCE923A-69AE-11D9-BED3-505054503030" = "Other Account Management Events";
"0CCE923C-69AE-11D9-BED3-505054503030" = "Directory Service Changes";
"0CCE923D-69AE-11D9-BED3-505054503030" = "Directory Service Replication";
"0CCE923E-69AE-11D9-BED3-505054503030" = "Detailed Directory Service Replication";
"0CCE923B-69AE-11D9-BED3-505054503030" = "Directory Service Access";
"0CCE9240-69AE-11D9-BED3-505054503030" = "Kerberos Service Ticket Operations";
"0CCE9241-69AE-11D9-BED3-505054503030" = "Other Account Logon Events";
"0CCE9242-69AE-11D9-BED3-505054503030" = "Kerberos Authentication Service";
"0CCE923F-69AE-11D9-BED3-505054503030" = "Credential Validation";
}

$UserRightsHash = @{
"SeTrustedCredManAccessPrivilege" = "Access_Credential_Manager_as_a_trusted_caller"
"SeNetworkLogonRight" = "Access_this_computer_from_the_network"
"SeTcbPrivilege" = "Act_as_part_of_the_operating_system"
"SeMachineAccountPrivilege" = "Add_workstations_to_domain"
"SeIncreaseQuotaPrivilege" = "Adjust_memory_quotas_for_a_process"
"SeInteractiveLogonRight" = "Allow_log_on_locally"
"SeRemoteInteractiveLogonRight" = "Allow_log_on_through_Remote_Desktop_Services"
"SeBackupPrivilege" = "Back_up_files_and_directories"
"SeChangeNotifyPrivilege" = "Bypass_traverse_checking" 
"SeSystemtimePrivilege" = "Change_the_system_time"
"SeTimeZonePrivilege" = "Change_the_time_zone"
"SeCreatePagefilePrivilege" = "Create_a_pagefile" 
"SeCreateTokenPrivilege" = "Create_a_token_object"
"SeCreateGlobalPrivilege" = "Create_global_objects"
"SeCreatePermanentPrivilege" = "Create_permanent_shared_objects"
"SeCreateSymbolicLinkPrivilege" = "Create_symbolic_links"
"SeDebugPrivilege" = "Debug_programs"
"SeDenyNetworkLogonRight" = "Deny_access_to_this_computer_from_the_network" 
"SeDenyBatchLogonRight" = "Deny_log_on_as_a_batch_job"
"SeDenyServiceLogonRight" = "Deny_log_on_as_a_service"
"SeDenyInteractiveLogonRight" = "Deny_log_on_locally"
"SeDenyRemoteInteractiveLogonRight" = "Deny_log_on_through_Remote_Desktop_Services"
"SeEnableDelegationPrivilege" = "Enable_computer_and_user_accounts_to_be_trusted_for_delegation"
"SeRemoteShutdownPrivilege" = "Force_shutdown_from_a_remote_system"
"SeAuditPrivilege" = "Generate_security_audits"
"SeImpersonatePrivilege" = "Impersonate_a_client_after_authentication"
"SeIncreaseWorkingSetPrivilege" = "Increase_a_process_working_set"
"SeIncreaseBasePriorityPrivilege" = "Increase_scheduling_priority"
"SeLoadDriverPrivilege" = "Load_and_unload_device_drivers"
"SeLockMemoryPrivilege" = "Lock_pages_in_memory"
"SeBatchLogonRight" = "Log_on_as_a_batch_job"
"SeServiceLogonRight" = "Log_on_as_a_service"
"SeSecurityPrivilege" = "Manage_auditing_and_security_log"
"SeRelabelPrivilege" = "Modify_an_object_label"
"SeSystemEnvironmentPrivilege" = "Modify_firmware_environment_values"
"SeManageVolumePrivilege" = "Perform_volume_maintenance_tasks"
"SeProfileSingleProcessPrivilege" = "Profile_single_process"
"SeSystemProfilePrivilege" = "Profile_system_performance"
"SeUndockPrivilege" = "Remove_computer_from_docking_station"
"SeAssignPrimaryTokenPrivilege" = "Replace_a_process_level_token"
"SeRestorePrivilege" = "Restore_files_and_directories"
"SeShutdownPrivilege" = "Shut_down_the_system"
"SeSyncAgentPrivilege" = "Synchronize_directory_service_data"
"SeTakeOwnershipPrivilege" =  "Take_ownership_of_files_or_other_objects"
}

$SecuritySettings = "MinimumPasswordAge","MaximumPasswordAge","MinimumPasswordLength","PasswordComplexity","PasswordHistorySize","LockoutBadCount","ForceLogoffWhenHourExpire","NewAdministratorName","NewGuestName","ClearTextPassword","LSAAnonymousNameLookup","EnableAdminAccount","EnableGuestAccount","ResetLockoutCount","LockoutDuration","MaxServiceAge","MaxTicketAge","MaxRenewAge","MaxClockSkew","TicketValidateClient"

# Create a variable so we can detect conflicts in the Configuration.
New-Variable -Name GlobalConflictEngine -Value @{} -Option AllScope -Scope Script -Force
$GlobalConflictEngine = @{}

# Create a variable so we can set DependsOn values between passes.
New-Variable -Name GlobalDependsOn -Value @() -Option AllScope -Scope Script -Force
$GlobalDependsOn = @()

# Create a variable to track every resource processed for Summary Data.
New-Variable -Name ProcessingHistory -Value @{} -Option AllScope -Scope Script -Force
$ProcessingHistory = @{}

# Global Flag to determine if correct Registry resource is available.
$ExclusiveFlagAvailable = $false

# This is the function that makes it all go. It has a variety of parameter sets which can be tricky to use.
# Each of the Switches tell the function what type of Code block it is creating.
# The additional parameters to the set are what determine the contents of the block.
# This Function takes the input and properly formats it with Tabs and Newlines so it looks properly formatted.
# It also has a built in Conflict detection engine.
# If it detects that a Resource with the Same REQUIRED values was already processed it will COMMENT OUT subsequent resource blocks with the same values.
# The Function also has logic to arbitrarily comment out a resource block if necessary (like disabled resources).
# The function also provides functionality to comment Code Blocks if comments are available.
Function Write-DSCString
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        # Configuration Block
        [Parameter(Mandatory=$true, ParameterSetName="Configuration")]
        [switch]$Configuration,

        # Resource Block
        [Parameter(Mandatory=$true, ParameterSetName="Resource")]
        [switch]$Resource,

        # Import-Module line.
        [Parameter(Mandatory=$true, ParameterSetName="ModuleImport")]
        [switch]$ModuleImport,

        # Node Block.
        [Parameter(Mandatory=$true, ParameterSetName="Node")]
        [switch]$Node,

        # Close out the configuration block.
        [Parameter(ParameterSetName="CloseConfigurationBlock")]
        [switch]$CloseConfigurationBlock,

        # Close the Node Block.
        [Parameter(ParameterSetName="CloseNodeBlock")]
        [switch]$CloseNodeBlock,

        # Invoke the Configuration (to create the MOF).
        [Parameter(Mandatory=$true, ParameterSetName="InvokeConfiguration")]
        [switch]$InvokeConfiguration,

        # This will be the Name of the Configuration Block or Node block or Resource Block.
        [Parameter(Mandatory=$true, ParameterSetName="Configuration")]
        [Parameter(Mandatory=$true, ParameterSetName="InvokeConfiguration")]
        [Parameter(Mandatory=$true, ParameterSetName="Node")]
        [Parameter(Mandatory=$true, ParameterSetName="Resource")]
        [string]$Name,

        # This is the TYPE to be used with Resource Blocks (Regisry, Service, etc.).
        [Parameter(Mandatory=$true, ParameterSetName="Resource")]
        [string]$Type,

        # This is an Array of ModuleNames to import.
        [Parameter(Mandatory=$true, ParameterSetName="ModuleImport")]
        [string[]]$ModuleName,

        # This is a hashtable of Keys/Values for a Resource Block.
        [Parameter(Mandatory=$true, ParameterSetName="Resource")]
        [hashtable]$Parameters,
        
        # This determines whether or not to comment out a resource block.
        [switch]$CommentOUT = $false,
        
        # This allows comments to be added to various code blocks.
        [string]$Comment,

        # This Output Path is for the Configuration Block, not this function.
        [Parameter(ParameterSetName="InvokeConfiguration")]
        [string]$OutputPath = $(Join-Path -Path $PSScriptRoot -ChildPath "Output")
    )
    
    $DSCString = ""
    switch ($PSCmdlet.ParameterSetName)
    {
        "Configuration" 
        { 
            # Add comments if there are comments.
            if ($PSBoundParameters.ContainsKey("Comment"))
            {
                $Comment = "`n<#`n$Comment`n#>"
            }
            else
            {
                $Comment = ""
            }
            
            # Output the DSC String.
            $DSCString = @"
$Comment
Configuration $Name`n{`n`n`t
"@
 
        }
        "ModuleImport" { 
            # Use this block to reset our Conflict Engine.
            $Script:GlobalConflictEngine = @{}
            # Loop through each module.
            foreach ($m in $ModuleName)
            {
                if (!(Get-command Get-DscResource -ErrorAction SilentlyContinue))
                {
                    Import-module PSDesiredStateConfiguration -Force
                }

                # Use Get-DSCResource to determine REQUIRED parameters to resource.
                $resources = Get-DscResource -Module $m -ErrorAction SilentlyContinue
                # Loop through every resource in the module.
                foreach ($r in $resources)
                {
                    # Add a blank entry for the Resource Block with Required Parameters.
                    $Script:GlobalConflictEngine[$r.Name] = @()
                    $tmpHash = @{}
                    $r.Properties.Where({$_.IsMandatory}) | % { $tmpHash[$_.Name] = ""}
                    $Script:GlobalConflictEngine[$r.Name] += $tmpHash

                    if ($r.Name -eq "Registry" -and $r.ModuleName -eq "PSDesiredStateConfiguration")
                    {
                        $ExclusiveFlagAvailable = $r.Properties.Name -contains 'Exclusive'
                    }
                }
            }
            
            # Output our Import-Module string.
            $DSCString = "Import-DSCResource -ModuleName $(($ModuleName | %{"'$_'"}) -join ", ")`n`t" }
        "Node" { $DSCString = "Node $Name`n`t{`n" }
        "InvokeConfiguration" { $DSCString = "$Name -OutputPath '$($OutputPath)'" }
        "CloseNodeBlock" { $DSCString = "`t}" }
        "CloseConfigurationBlock" { $DSCString = "`n}`n" }          
        "Resource"
        {
            # Variables to be used for commeting out resource if necessary.
            $CommentStart = ""
            $CommentEnd = ""
            
            # Set our conflict variables up.
            $GlobalConflict = $false
            $Conflict = @()
            $Checked = @()
            # Determine if we have already processed a Resource Block of this type.
            if ($Script:GlobalConflictEngine.ContainsKey($Type))
            {
                # Loop through every Resource definition of this type.
                foreach ($hashtable in $Script:GlobalConflictEngine[$Type])
                {
                    $Conflict = @()
                    $Checked = @()
                    
                    # Loop through every Key/Value Pair in the Resource definition to see if they match the current one.
                    foreach ($KeyPair in $hashtable.GetEnumerator())
                    {
                        # Add the test result to our Conflict Array.
                        $Conflict += $KeyPair.Value -eq $Parameters[$KeyPair.Name]    
                        # Need to store which Key/Value Pairs we checked.
                        $Checked += $KeyPair.Name
                    }
                    
                    # If we found a conflict.
                    if ($Checked.Count -gt 0 -and $Conflict -notcontains $false)
                    {
                        Write-Verbose "Detecting Potential Conflict in $Name. Commenting Out Block"
                        $GlobalConflict = $true
                        break
                    }
                }
            }

            # If we do not have a processing history entry for this type, set it to a blank array.
            # Have to do this here, because they may have forgotten do import the module so we cannot assume only Resources specified in the Import-Module.
            if (!$ProcessingHistory.ContainsKey($Type))
            {
                $ProcessingHistory[$Type] = @()
            }

            # Add this resource to the processing history.
            $ProcessingHistory[$Type] += @{Name=$Name;Conflict=$GlobalConflict;Disabled=$CommentOut}
            
            # If we have a conflict, comment the block out.
            if ($GlobalConflict)
            {
                $CommentOUT = $true
            }
            else # Add this Resources Key/Value pairs to the collective.
            {
                $tmpHash = @{}
                foreach ($Check in $Checked)
                {
                    $tmpHash[$check] = $Parameters[$Check]
                }

                $Script:GlobalConflictEngine[$Type] += $tmpHash
            }
            
            # If we are commenting this block out, then set up our comment characters.
            if ($CommentOut)
            {
                $CommentStart = "<#"
                $CommentEnd = "#>"
            }

            # If they passed a comment FOR the block, add it above the block.
            if ($PSBoundParameters.ContainsKey("Comment"))
            {
                $tmpComment = "<#`n"
                $Comment -split "`n" | %{ $tmpComment += "`t`t$_`n"}
                $tmpComment += "`t`t#>`n`t`t"
                $Comment = $tmpComment
            }
            else
            {
                $Comment = ""
            }

            # Start the Resource Block with Comment and CommentOut characters if necessary.
            $DSCString = "`t`t$Comment$($CommentStart)$Type '$($Name)'`n`t`t{"
            foreach ($key in $Parameters.Keys)
            {
                # This was extremely tricky. Have to determine the type of object passed so we can format it properly.
                # MOF files are picky with their Types so a BOOl has to be $True/$False not "True"/"False" or "$True"/"$False"
                # Arrays have to be separated and individually contained in quotes.
                # Numbers have to have no quotes so they are not processed as strings.
                if ($Parameters[$key] -is [Array]) 
                {
                    $item = $Parameters[$key]
                    $DSCString += "`n`t`t`t$($key) = "

                    if ($item.Count -gt 0)
                    {
                        for ($i = 0; $i -lt $item.Count;$i++)
                        {
                            if ($item[$i] -is [String])
                            {
                                Try
                                {
                                    Invoke-Expression $("`$variable = '" + $item[$i].TrimStart("'").TrimEnd("'").TrimStart('"').TrimEnd('"').Trim() + "'") | Out-Null
                                    $DSCString += "'$($item[$i].TrimStart("'").TrimEnd("'").TrimStart('"').TrimEnd('"').Trim())'"   
                                }
                                catch
                                {
                                    $DSCString += "@'`n$($item[$i].TrimStart("'").TrimEnd("'").TrimStart('"').TrimEnd('"').Trim())`n'@"   
                                }
                            }
                            else
                            {
                                $DSCString += "$($item[$i])"   
                            }

                            if (($item.Count - $i) -gt 1)
                            {
                                $DSCString += ", "
                            }
                        }
                    }
                    else
                    {
                        $DSCString += "@()"
                    }
                }
                elseif($Parameters[$key] -is [System.String]) 
                { 
                    Try
                    {
                        Invoke-Expression $("`$variable = '" + $Parameters[$Key].TrimStart("'").TrimEnd("'").TrimStart('"').TrimEnd('"') + "'") | Out-Null
                        $DSCString += "`n`t`t`t$($key) = '$([string]::new($Parameters[$key].TrimStart("'").TrimEnd("'").TrimStart('"').TrimEnd('"').Trim()))'" 
                    }
                    Catch
                    {
                        # Parsing Error
                        $DSCString += "`n`t`t`t$($key) = @'`n$($Parameters[$key].Trim("'").TrimStart("'").TrimEnd("'").TrimStart('"').TrimEnd('"'))`n'@" 
                    }
                }
                elseif($Parameters[$key] -is [System.Boolean])
                { 
                    $DSCString += "`n`t`t`t$($key) = `$$([string]([bool]$Parameters[$key]))" 
                }
                else
                {
                    $DSCString += "`n`t`t`t$($key) = $($Parameters[$key])" 
                }
            }
             
            # Output our Resource Block String
            $DSCString += "`n`n`t`t}$CommentEnd`n`n"
        }
    }

    Write-Verbose $DSCString

    # Return our DSCstring.
    return $DSCString
}

# XML helper function to grab Node comments from SCMXML and put them into easily convertable stringdata.
Function Get-NodeComments
{
    [CmdletBinding()]
    [OutputType([string])]
    param
    (
        [Parameter(Mandatory=$true)]
        [System.XML.XmlElement]$Node
    )

    # Grab all of the comments.
    $Setting = "../.."
    $ProductInfo = $node.SelectNodes($Setting).Content.ProductInfo
    $Comments = $ProductInfo | Out-String
    $Comments = $Comments -replace ": ProductRef", ": $($ProductInfo.ProductRef.product_ref)"
    $Comments = ($Comments -replace "ManualTestProcedure :", "") -replace "ValueRange : ValueRange", ""
    $Comments = $Comments -replace ": CCEID-50", ": $($ProductInfo.'CCEID-50'.ID)"
    $Comments = $Comments.Trim()
    <# This is how to get string data into object format.
        (((($Comments -replace ": ", "= '") -replace "(`n)([A-Z])", ("'`$1" + '$2')) + '"') -replace "`n[^A-Z]", "") -replace "\\", "\\" | ConvertFrom-StringData
    #>


    return $Comments
}

# Reverse function to get StringData Object from Comments String output of Get-NodeComments.
Function Get-NodeDataFromComments
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [String]$Comments
    )

    Try 
    {
        $comments = $comments -replace "[^\u0000-\u007F]", ""
        $comments = ((((($Comments -replace "(?m)^([^ ]*)\s*:\s?", "`$1 = '") -replace "(?m)^[^A-Z]*`$`n", "") -replace "(?m)`$(`n)([A-Z])", ("'`$1" + '$2')) + '"')) -replace "\\", "\\"
        $tmpComments = $Comments -split "'`n"
        $Comments = ($tmpComments | %{ (($_ -replace "`n", "") + "'") -replace "'", "" }) -join "`n"
        $object = $comments | ConvertFrom-StringData
    }
    Catch
    {
        Write-Error "Cannot convert COMMENT Data"
    }

    return $object
}

# Helper function to convert INI into hashtable.
function Get-IniContent 
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName=$true)]
        [ValidateScript({Test-Path $_})]
        [string]$Path
    )

    $ini = @{}
    switch -regex -file $Path
    {
        “^\[(.+)\]” # Section
        {
            $section = $matches[1]
            $ini[$section] = @{}
            $CommentCount = 0
        }
        “^(;.*)$” # Comment
        {
            $value = $matches[1]
            $CommentCount = $CommentCount + 1
            $name = “Comment” + $CommentCount
            $ini[$section][$name] = $value
            continue
        } 
        “(.+?)\s*=(.*)” # Key
        {
            $name,$value = $matches[1..2]
            $ini[$section][$name] = $value
            # Need to replace double quotes with `"
            continue
        }
        "\`"(.*)`",(.*)$" 
        { 
            $name, $value = $matches[1..2]
            $ini[$section][$name] = $value
            continue
        }
    }
    return $ini
}

Function Write-CSVAuditData
{
    [CmdletBinding()]
    [OutputType([string])]
    param
    (
        [Parameter(Mandatory=$true)]
        [System.Object]$Entry
    )

    $retHash = @{}
    $retHash.Name = $Entry.SubCategory

    switch -regex ($Entry.InclusionSetting)
    {
        "Success and Failure"
        {
            $retHash.Ensure = "Present"
            $retHash.AuditFlag = "Success"
            Write-DSCString -Resource -Name "$Name (Success) - Inclusion" -Type AuditPolicySubcategory -Parameters $retHash
            $retHash.AuditFlag = "Failure"
            Write-DSCString -Resource -Name "$Name (Failure) - Inclusion" -Type AuditPolicySubcategory -Parameters $retHash
        }

        "No Auditing"
        {
            $retHash.Ensure = "Absent"
            $retHash.AuditFlag = "Success"
            Write-DSCString -Resource -Name "$Name (Success) - Inclusion" -Type AuditPolicySubcategory -Parameters $retHash
            $retHash.AuditFlag = "Failure"
            Write-DSCString -Resource -Name "$Name (Failure) - Inclusion" -Type AuditPolicySubcategory -Parameters $retHash
        }

        "^(Success|Failure)$"
        {
            $retHash.Ensure = "Present"
            $retHash.AuditFlag = $Entry.InclusionSetting
            Write-DSCString -Resource -Name "$Name - Inclusion" -Type AuditPolicySubcategory -Parameters $retHash
        }
    }
    
    $retHash.Ensure = "Absent"
    switch -regex ($Entry.ExclusionSetting)
    {
        "Success and Failure"
        {
            $retHash.Ensure = "Absent"
            $retHash.AuditFlag = "Success"
            Write-DSCString -Resource -Name "$Name (Success) - Exclusion" -Type AuditPolicySubcategory -Parameters $retHash
            $retHash.AuditFlag = "Failure"
            Write-DSCString -Resource -Name "$Name (Failure) - Exclusion" -Type AuditPolicySubcategory -Parameters $retHash
        }

        "No Auditing"
        {
            # I am not sure how to make sure that "No Auditing" is Excluded or ABSENT. What should it be set to then?
        }

        "^(Success|Failure)$"
        {
            $retHash.Ensure = "Absent"
            $retHash.AuditFlag = $Entry.ExclusionSetting
            Write-DSCString -Resource -Name "$Name - Exclusion" -Type AuditPolicySubcategory -Parameters $retHash
        }
    }
}

Function Write-GPORegistryXMLData
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory=$true)]
        [System.Xml.XmlElement]$XML    
    )
    
    $regHash = @{}
    $regHash.ValueType = "None"
    $regHash.ValueName = ""
    $regHash.ValueData = ""
    $regHash.Key = ""

    $Properties = $XML.Properties

    $ValueData = 1
    if (!([int]::TryParse($Properties.Value, [ref]$ValueData)))
    {
        $ValueData = "'$($Properties.ValueData)'" -replace "[^\u0020-\u007E]", ""
    }

    $regHash.ValueData = $ValueData 
    $regHash.ValueName = $Properties.name -replace "[^\u0020-\u007E]", ""

    switch ($Properties.hive)
    {
        "HKEY_LOCAL_MACHINE" { $regHash.Key = "HKLM:\" }
    }

    $regHash.Key = Join-Path -Path $regHash.Key -ChildPath $Properties.Key

    switch ($Properties.type)
    {
        "REG_SZ" { $reghash.ValueType = "String" }
        "REG_NONE" { $reghash.ValueType = "None" }
        "REG_EXPAND_SZ" { $reghash.ValueType = "ExpandString" }
        "REG_DWORD" { $reghash.ValueType = "DWORD" }
        "REG_QWORD" { $reghash.ValueType = "QWORD" }    
        "REG_BINARY" { $reghash.ValueType = "Binary" }  
        "REG_MULTI_SZ" { $reghash.ValueType = "MultiString" }
        Default { $regHash.ValueType = "None" }
    }

    if ($regHash.ValueType -eq "DWORD" -and ($ValueData -match "(Disabled|Enabled|Not Defined|True|False)" -or $ValueData -eq "''"))
    {
        # This is supposed to be an INT and it's a String
        [int]$regHash.ValueData = @{"Disabled"=0;"Enabled"=1;"Not Defined"=0;"True"=1;"False"=0;''=0}.$ValueData
    }
    elseif ($regHash.ValueType -eq "String" -or $regHash.ValueType -eq "MultiString")
    {
        [string]$regHash.ValueData = [string]$ValueData
    }

    if ($regHash.ValueType -eq "None")
    {
        # The REG_NONE is not allowed by the Registry resource.
        $regHash.Remove("ValueType")
    }

    if ([string]::IsNullOrEmpty($regHash.ValueName))
    {
        $regHash.Remove("ValueData")
    }

    $CommentOUT = $false
    
    Write-DSCString -Resource -Name "XML_$(Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName)" -Type Registry -Parameters $regHash -CommentOUT:$CommentOUT
}

Function Write-POLRegistryData
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [psobject]$Data    
    )

    $regHash = @{}
    $regHash.ValueName = ""
    $regHash.Key = "HKLM:\"

    $regHash.ValueName = $Data.ValueName -replace "[^\u0020-\u007E]", ""
    $regHash.Key = Join-Path -Path $regHash.Key -ChildPath $Data.KeyName
    
    $CommmentOUT = $false
   
    # Process any pol instructions.
    switch -regex ($regHash.ValueName)
    {
        "\*\*del\.(?<ValueName>.*)$" 
        {
            $regHash.ValueName = $Matches.ValueName
            $regHash.Ensure = "Absent"
            $Name = "DEL_$((Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName).TrimStart("HKLM:"))"
            
            $script:GlobalDependsOn += $Name

            # This is only a delete so return from here.
            return Write-DSCString -Resource -Name $Name  -Type Registry -Parameters $regHash -CommentOUT:$CommentOUT
        }

        "\*\*delvals\."
        {
            $regHash.Ensure = "Present"
            $regHash.ValueName = ""
            $regHash.Exclusive = $true
            $Name = "DELVALS_$($regHash.Key.TrimStart("HKLM:"))"

            $script:GlobalDependsOn += $Name
            # This is only a delete so return from here.
            return Write-DSCString -Resource -Name $Name  -Type Registry -Parameters $regHash -CommentOUT:(!$ExclusiveFlagAvailable)
        }

        "\*\*DeleteValues"
        {
            $Data.ValueData = $Data.ValueData -replace "[^\u0020-\u007E]", ""
            foreach ($ValueName in ($Data.ValueData -split ";"))
            {          
                $regHash.Ensure = "Absent"
                $Name = "DELETEVALUES_$((Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName).TrimStart("HKLM:"))"

                $script:GlobalDependsOn += $Name

                # This is only a delete so return from here.
                Write-DSCString -Resource -Name $Name  -Type Registry -Parameters $regHash -CommentOUT:$CommentOUT
            }

            return
        }

        "\*\*DeleteKeys"
        {
            $Data.ValueData = $Data.ValueData -replace "[^\u0020-\u007E]", ""
            foreach ($Key in ($Data.ValueData -split ";"))
            {          
                $regHash.Ensure = "Absent"
                $Name = "DELETEKEYS_$($regHash.Key.TrimStart("HKLM:"))"

                $script:GlobalDependsOn += $Name

                # This is only a delete so return from here.
                Write-DSCString -Resource -Name $Name  -Type Registry -Parameters $regHash -CommentOUT:$CommentOUT
            }

            return
        }

        "\*\*SecureKey"
        {
            $Name = "SECUREKEY_$($regHash.Key)"

            # This is only a delete so return from here.
            return Write-DSCString -Resource -Name $Name  -Type Registry -Parameters $regHash -CommentOUT:$true
        }
    }

    # Now setup the rest of the Params Hashtable values.
    $regHash.ValueType = "None"
    $regHash.ValueData = ""

    $ValueData = 1
    if (!([int]::TryParse($Data.ValueData, [ref]$ValueData)))
    {
        $ValueData = "'$($Data.ValueData)'" -replace "[^\u0020-\u007E]", ""
    }

    $regHash.ValueData = $ValueData
    switch ($Data.ValueType)
    {
        "REG_SZ" { $reghash.ValueType = "String" }
        "REG_NONE" { $reghash.ValueType = "None" }
        "REG_EXPAND_SZ" { $reghash.ValueType = "ExpandString" }
        "REG_DWORD" { $reghash.ValueType = "DWORD" }
        "REG_QWORD" { $reghash.ValueType = "QWORD" }    
        "REG_BINARY" { $reghash.ValueType = "Binary" }  
        "REG_MULTI_SZ" { $reghash.ValueType = "MultiString" }
        Default { $regHash.ValueType = "None" }
    }

    if ($regHash.ValueType -eq "DWORD" -and ($ValueData -match "(Disabled|Enabled|Not Defined|True|False)"  -or $ValueData -eq "''"))
    {
        # This is supposed to be an INT and it's a String
        [int]$regHash.ValueData = @{"Disabled"=0;"Enabled"=1;"Not Defined"=0;"True"=1;"False"=0;''=0}.$ValueData
    }
    elseif ($regHash.ValueType -eq "String"  -or $regHash.ValueType -eq "MultiString")
    {
        [string]$regHash.ValueData = [string]$ValueData
    }

    if ($regHash.ValueType -eq "None")
    {
        # The REG_NONE is not allowed by the Registry resource.
        $regHash.Remove("ValueType")
    }

    if ([string]::IsNullOrEmpty($regHash.ValueName))
    {
        $regHash.Remove("ValueData")
    }

    $DependsOn = @()
    $delVals = "DELVALS_$($regHash.Key.TrimStart("HKLM:"))"
    if ($script:GlobalDependsOn -contains $delVals -and $ExclusiveFlagAvailable)
    {
        $DependsOn += "[Registry]$delVals"
    }

    $delVal_ValueName = "DEL_$((Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName).TrimStart("HKLM:"))"
    if ($script:GlobalDependsOn -contains $delVal_ValueName)
    {
        $DependsOn += "[Registry]$delVal_ValueName"
    }

    $deleteKeys = "DELETEKEY_$($regHash.Key.TrimStart("HKLM:"))"
    if ($script:GlobalDependsOn -contains $deleteKeys)
    {
        $DependsOn += "[Registry]$deleteKeys"
    }

    $deleteValue = "DELETEVALUES_$((Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName).TrimStart("HKLM:"))"
    if ($script:GlobalDependsOn -contains $deleteValue)
    {
        $DependsOn += "[Registry]$deleteValue"
    }

    if ($DependsOn.count -gt 0)
    {
        $regHash.DependsOn = $DependsOn
    }

    Write-DSCString -Resource -Name (Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName) -Type Registry -Parameters $regHash -CommentOUT:$CommentOUT
}

Function Write-INFRegistryData
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Key,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$ValueData
    )

    $regHash = @{}
    $regHash.ValueType = "None"
    $regHash.ValueName = ""
    $regHash.ValueData = ""
    $regHash.Key = ""

    $values = $ValueData -split ","
            
    $KeyPath = $Key
            
    $ValueName = Split-Path -Leaf $KeyPath
    $regHash.ValueName = $ValueName -replace "[^\u0020-\u007E]", ""
    $regHash.Key = Split-Path -Parent $KeyPath
    $regHash.Key = $regHash.Key -replace "MACHINE\\", "HKLM:\" 
    $values[1] = $values[1..$values.count] -join ","
        
    switch ($values[0]) 
    { 
        "1" 
        { 
            # Not sure what type the legal caption is. Is it an array of strings?
            $regHash.ValueData = "'$($values[1])'" -replace "[^\u0020-\u007E]", ""
            $regHash.ValueType = "String"
        } 
                        
        "7" 
        { 
            $regHash.ValueData = @"
$($values[1])
"@
 -replace "[^\u0020-\u007E]", ""
            $regHash.ValueType = "MultiString"
        }
                        
        "4" 
        { 
            $tmpValueData = 1
            if (!([int]::TryParse($values[1], [ref]$tmpValueData)))
            {
                Write-Error "Cannot Parse Value for $ValueData at key $KeyData, setting value to 0"
                $tmpValueData = 0
            }

            $regHash.ValueData = $tmpValueData
            $regHash.ValueType = "DWORD"
        }
                        
        "3" 
        { 
            $hexified = $values[1] -split "," | % { "0x$_"}
            $regHash.ValueData = [byte[]]$hexified
            $regHash.ValueType = "Binary"
        } 

        Default
        {
            Write-Warning "Cannot parse RegistryINF Data"
            return ""
        }
    }

    if ($regHash.ValueType -eq "DWORD" -and ($regHash.ValueData -match "(Disabled|Enabled|Not Defined|True|False)"  -or $regHash.ValueData -eq "''"))
    {
        # This is supposed to be an INT and it's a String
        [int]$regHash.ValueData = @{"Disabled"=0;"Enabled"=1;"Not Defined"=0;"True"=1;"False"=0;''=0}.$regHash.ValueData
    }
    elseif ($regHash.ValueType -eq "String" -or $regHash.ValueType -eq "MultiString")
    {
        [string]$regHash.ValueData = [string]$regHash.ValueData
    }
    
    if ($regHash.ValueType -eq "None")
    {
        # The REG_NONE is not allowed by the Registry resource.
        $regHash.Remove("ValueType")
    }

    if ([string]::IsNullOrEmpty($regHash.ValueName))
    {
        $regHash.Remove("ValueData")
    }

    $CommentOUT = $false

    Write-DSCString -Resource -Name "INF_$(Join-Path -Path $regHash.Key -ChildPath $regHash.ValueName)" -Type Registry -Parameters $regHash -CommentOUT:$CommentOUT
}

Function Write-INFServiceData
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$Service,

        [Parameter(Mandatory = $true)]
        [string]$ServiceData
    )

    $serviceHash = @{}
    $serviceHash.Name = ""
    $serviceHash.State = ""

    $values = $ServiceData -split ","
    $serviceHash.Name = $Service
    
    switch ($values[0]) 
    { 
        "2" { $serviceHash.State = "Running" } 
        "4" { $serviceHash.State = "Stopped" } 
        "3" { $serviceHash.StartupType = "Manual"
              $serviceHash.Remove("State") 
            } 
    }

    # Does the Second (if present) value determine starttype?
    
    Write-DSCString -Resource -Name "INF_$($serviceHash.Name)" -Type Service -Parameters $serviceHash    
}

Function Write-INFFileSecurityData
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$ACLData
    )

    $aclHash = @{}
    $aclHash.Path = ""
    $aclHash.DACLString = ""

    # These are DACLS
    $aclHash.Path = ((($Path -replace "^%", "`env:") -replace "%\\", "\") -replace "%", "")
    if ($aclData -match "[0-9],(.*)$")
    {
        $aclHash.DACLString = $Matches[1]
    }
    else
    {
        Write-Error "Cannot Parse $ACLData for $Path"
        return ""
    }
    
    Write-DSCString -Resource -Name "INF_$($ACLhash.Path)_ACL" -Type ACL -Parameters $aclHash
}

Function Write-INFPrivilegeData
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Privilege,

        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [string]$PrivilegeData
    )

    $privilegeHash = @{}
    $privilegeHash.Policy = ""
    $privilegeHash.Identity = ""

    # These are UserRights
    if ($UserRightsHash.ContainsKey($Privilege))
    {                        
        $privilegeHash.Policy = $UserRightsHash[$Privilege]
    }
    else
    {
        Write-Error "Cannot find $Privilege"
        return ""
    }

    $privilegeHash.Identity = $PrivilegeData
    
    Write-DSCString -Resource -Name "INF_$($privilegeHash.Policy)" -Type UserRightsAssignment -Parameters  $privilegeHash
}

Function Write-INFRegistryACLData
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$Path,

        [Parameter(Mandatory = $true)]
        [string]$ACLData
    )

    $regHash = @{}
    $regHash.Path = ""
    $regHash.DACLString = ""
                   
    $regHash.Path = $Path -replace "MACHINE\\", "HKLM:\"
    if ($ACLData -match "[0-9],(.*)$")
    {
        $regHash.DACLString = $Matches[1]
    }
    else
    {
        Write-Error "Cannot parse $ACLData for $Key"
        return ""
    }
    
    Write-DSCString -Resource -Name "INF_$($regHash.Path)_ACL" -Type ACL -Parameters $regHash
}

Function Write-INFSecuritySettingData
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$Key,

        [Parameter(Mandatory = $true)]
        [string]$SecurityData
    )

    $regHash = @{}              
    $SecurityData = $SecurityData.Trim()

    [int]$ValueData = 1
    if (![int]::TryParse($SecurityData, [ref]$ValueData))
    {
        [string]$ValueData = $SecurityData
    }

    $params = @{$key = $ValueData;Name = $Key}
    Write-DSCString -Resource -Name "INF_$Key" -Type SecuritySetting -Parameters $params
}

Function Write-INFGroupData
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.Collections.DictionaryEntry]$GroupData
    ) 
}

Function Complete-Configuration
{
    [CmdletBinding()]
    [OutputType([bool])]
    param
    (
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$ConfigString,

        [Parameter()]
        [string]$OutputPath = $(Join-Path -Path $pwd.path -ChildPath "Output")
    )
    
    if ($ConfigString -match "(?m)Configuration (?<Name>.*)$")
    {
        $CallingFunction = $Matches.Name
    }
    else
    {
        $CallingFunction = (Get-PSCallStack)[1].Command
    }

    if (!(Test-Path $OutputPath))
    {
        mkdir $OutputPath
    }
    
    $scriptblock = [scriptblock]::Create($ConfigString)
    
    if (!$?)
    {
        # Somehow CallingFunction, defined above, is not useable right here. Not sure why
        $Path = $(Join-Path -Path $OutputPath -ChildPath "$($CallingFunction).ps1.error")
        $ConfigString > $Path
        
        Write-Error "Could not CREATE ScriptBlock from configuration. Creating PS1 Error File: $Path. Please rename error file to PS1 and open for further inspection. Errors are listed below"
        return $false
    }
    
    $output = Invoke-Command -ScriptBlock $scriptblock
    
    if (!$?)
    {
        $Path = $(Join-Path -Path $OutputPath -ChildPath "$($CallingFunction).ps1.error")
        $scriptblock.ToString() > $Path
        
        Write-Error "Could not COMPILE cofiguration. Storing ScriptBlock: $Path. Please rename error file to PS1 and open for further inspection. Errors are listed below"
        foreach ($e in $output)
        {
            Write-Error $e
        }
        return $false
    }
        
    return $true
}


Function Write-XMLRegistryData
{
    [CmdletBinding()]
    [OutputType([string])]
    param
    (
        [Parameter(Mandatory=$true)]   
        [System.Xml.XmlElement]$DiscoveryData,

        [Parameter(Mandatory=$true)]
        [System.Xml.XmlElement]$ValueData
    )

    # Grab the ExistensialRule and Validation Rule.
    $ExistensialRule = $valueData.SelectNodes("..").ExistentialRule
    $ValidationRules = $valueData.SelectNodes("..").ValidationRules   
           
    $Comments = Get-NodeComments -Node $DiscoveryData
    $Name = $DiscoveryData.SelectNodes("../..").Name
    $Name = "$((Get-NodeDataFromComments -Comments $Comments).'CCEID-50'): $Name"

    # This means that there is Policy Data
    if ($DiscoveryData.ChildNodes.name -notcontains "RegistryDiscoveryInfo")
    {
        $PolicyData = $DiscoveryData.SelectNodes("../..").Policy
                
        $Hive = switch ($DiscoveryData.Scope) { "Machine" { "HKLM" } }
        
        $Settings = ""
        switch ($PolicyData.Elements)
        {
            {$_.Enum} { $Settings = "Enum"}
            {$_.Boolean} { $Settings = "Boolean" } 
            {$_.Text} { $Settings = "Text" }
            {$_.Decimal} {$Settings = "Decimal" }
            Default { Write-Error "Cannot find Proper Policy Value for $Name"; return "" } 
        }
        
        foreach ($Setting in $PolicyData.Elements."$Settings")
        {
            $retHash = @{}
            $retHash.ValueName = $Setting.ValueName
            $retHash.Key = Join-Path -Path "$($Hive):" -ChildPath $Setting.Key
            
            $value = 1
                        
            [psobject]$TmpValue = ($ValidationRules.OptionRule | ?{$_.Id -eq $Setting.Id}).Value.ValueA
            if ($TmpValue -match '$\(string')
            {
                $TmpValue = $Settings.Item | ?{$_.DisplayName -match $TmpValue}
            }
            
            switch -Regex ($TmpValue)
            {
                {[string]::IsNullOrEmpty($_)} { Write-Error "Cannot Parse Data for $Name"; return ""}
                "(Disabled|Enabled|Not Defined|True|False)" { [int]$TmpValue = @{"Disabled"=0;"Enabled"=1;"Not Defined"=0;"True"=1;"False"=0;''=0}.$Value; $retHash.ValueType = "DWORD"; break }
                "''" { [int]$TmpValue = @{"Disabled"=0;"Enabled"=1;"Not Defined"=0;"True"=1;"False"=0;''=0}.$Value; $retHash.ValueType = "DWORD"; break }
                {[int]::TryParse($TmpValue, [ref]$value)} { [int]$TmpValue = $Value; $retHash.ValueType = "DWORD";break}
                Default { [string]$TmpValue = $TmpValue -replace "[^\u0020-\u007E]", ""; $retHash.ValueType = "String"}
            }
            
            $retHash.ValueData = $TmpValue
            
            Write-DSCString -Resource -Name "$($Name): $($setting.Id)" -Type Registry -Parameters $retHash -Comment $Comments
        }
    }
    else
    {
        $retHash = @{}
        $retHash.Key = ""
        $retHash.ValueName = ""
        $retHash.ValueData = ""
        
        # Grab the Value and Operator
        $TempValue = $ValidationRules.SettingRule.Value.ValueA
        $Operator = $ValidationRules.SettingRule.Operator

        if ($DiscoveryData.RegistryDiscoveryInfo.Hive -is [System.XML.XMLElement])
        {
            $Hive = switch ($DiscoveryData.RegistryDiscoveryInfo.Hive."#text") { "HKEY_LOCAL_MACHINE" { "HKLM" } }
            $ValueType = $DiscoveryData.RegistryDiscoveryInfo.DataType."#text"
            $KeyPath = $DiscoveryData.RegistryDiscoveryInfo.KeyPath."#text"
            $ValueName = $DiscoveryData.RegistryDiscoveryInfo.ValueName."#text"
        }
        else
        {
            $Hive = switch ($DiscoveryData.RegistryDiscoveryInfo.Hive) { "HKEY_LOCAL_MACHINE" { "HKLM" } }
            $ValueType = $DiscoveryData.RegistryDiscoveryInfo.DataType
            $KeyPath = $DiscoveryData.RegistryDiscoveryInfo.KeyPath
            $ValueName = $DiscoveryData.RegistryDiscoveryInfo.ValueName
        }

        $retHash.Key = Join-Path -Path "$($Hive):" -ChildPath $KeyPath
        $retHash.ValueName = $ValueName

        $Value = 1
    
        if (!([int]::TryParse($TempValue, [ref]$Value)))
        {
            $Value = "'$($TempValue)'" -replace "[^\u0020-\u007E]", ""
        }
    
        switch ($ValueType)
        {
            "REG_SZ" { $ValueType = "String" }
            "REG_NONE" { $ValueType = "None" }
            "REG_EXPAND_SZ" { $ValueType = "ExpandString" }
            "REG_DWORD" { $ValueType = "DWORD" }
            "REG_QWORD" { $ValueType = "QWORD" }    
            "REG_BINARY" { $ValueType = "Binary" }  
            "REG_MULTI_SZ" { $ValueType = "MultiString" }
            Default { $ValueType = "None" }
        }

        if ($ValueType -eq "DWORD" -and ($Value -match "(Disabled|Enabled|Not Defined|True|False)" -or $ValueData -eq "''"))
        {
            # This is supposed to be an INT and it's a String
            [int]$Value = @{"Disabled"=0;"Enabled"=1;"Not Defined"=0;"True"=1;"False"=0;''=0}.$Value
        }
        elseif ($ValueType -eq "String"  -or $ValueType -eq "MultiString")
        {
            [string]$Value = [string]$Value
        }
                
        $retHash.ValueType = $ValueType
        $retHash.ValueData = $Value
        
        if ($regHash.ValueType -eq "None")
        {
            # The REG_NONE is not allowed by the Registry resource.
            $regHash.Remove("ValueType")
        }

        if ([string]::IsNullOrEmpty($regHash.ValueName))
        {
            $regHash.Remove("ValueData")
        }

        Write-DSCString -Resource -Name $Name -Type Registry -Parameters $retHash -Comment $Comments
    }                
}

Function Write-XMLAuditData
{
    [CmdletBinding()]
    [OutputType([hashtable])]
    param
    (
        [Parameter(Mandatory=$true)]   
        [System.Xml.XmlElement]$DiscoveryData,

        [Parameter(Mandatory=$true)]
        [System.Xml.XmlElement]$ValueData
    )
    
    # Grab the ExistensialRule and Validation Rule.
    $ExistensialRule = $valueData.SelectNodes("..").ExistentialRule
    $ValidationRules = $valueData.SelectNodes("..").ValidationRules
    
    $Comments = Get-NodeComments -Node $DiscoveryData
    $Name = $DiscoveryData.SelectNodes("../..").Name
    $Name = "$((Get-NodeDataFromComments -Comments $Comments).'CCEID-50'): $Name"
            
    # Grab the Value and Operator
    $TempValue = $ValidationRules.SettingRule.Value.ValueA
    $Operator = $ValidationRules.SettingRule.Operator

    $retHash = @{}
    $retHash.AuditFlag = ""
    $retHash.Name = ""
        
    $AuditFlag = ((("$($TempValue)" -replace "[^\u0020-\u007E]", "") -replace "Success And Failure", "SuccessAndFailure") -replace "No Auditing", "NoAuditing")
    $AuditID = $DiscoveryData.AdvancedAuditDiscoveryInfo.advancedauditsettingid.Trim("{").TrimEnd("}")

    if ($AuditCategoryHash.ContainsKey($AuditID))
    {
        $retHash.Name = $AuditCategoryHash["$AuditID"]
    }
    else
    {
        Write-Error "Cannot parse Subcategory for $AuditID with AuditFlag ($AuditFlag)"
        return ""
    }

    if (![string]::IsNullOrEmpty($AuditFlag))
    {
        $retHash.AuditFlag = $AuditFlag    
    }
    
    switch ($retHash.AuditFlag)
    {
        "SuccessAndFailure" 
        {
            $retHash.AuditFlag = "Success"
            Write-DSCString -Resource -Name "$Name (Success)" -Type AuditPolicySubcategory -Parameters $retHash -Comment $Comments
            $retHash.AuditFlag = "Failure"
            Write-DSCString -Resource -Name "$Name (Failure)" -Type AuditPolicySubcategory -Parameters $retHash -Comment $Comments
        }
        
        "NoAuditing" 
        {
            $retHash.Ensure = "Absent"
            $retHash.AuditFlag = "Success"
            Write-DSCString -Resource -Name "$Name (Success)" -Type AuditPolicySubcategory -Parameters $retHash -Comment $Comments
            $retHash.AuditFlag = "Failure"
            Write-DSCString -Resource -Name "$Name (Failure)" -Type AuditPolicySubcategory -Parameters $retHash -Comment $Comments
        } 

        Default
        {
            Write-DSCString -Resource -Name $Name -Type AuditPolicySubcategory -Parameters $retHash -Comment $Comments
        }
    }
}

Function Write-XMLPrivilegeData
{
    [CmdletBinding()]
    [OutputType([hashtable])]
    param
    (
        [Parameter(Mandatory=$true)]   
        [System.Xml.XmlElement]$DiscoveryData,
        
        [Parameter(Mandatory=$true)]   
        [System.Xml.XmlElement]$ValueData
    )
    
    # Grab the ExistensialRule and Validation Rule.
    $ExistensialRule = $valueData.SelectNodes("..").ExistentialRule
    $ValidationRules = $valueData.SelectNodes("..").ValidationRules
       
    $Comments = Get-NodeComments -Node $DiscoveryData
    $Name = $DiscoveryData.SelectNodes("../..").Name
    $Name = "$((Get-NodeDataFromComments -Comments $Comments).'CCEID-50'): $Name"
         
    # Grab the Value and Operator
    $TempValue = $ValidationRules.SettingRule.Value.ValueA
    $Operator = $ValidationRules.SettingRule.Operator

    $retHash = @{}
    
    $retHash.Identity = @()
    $retHash.Policy = ""

    if ($DiscoveryData.WmiDiscoveryInfo.Where -match "UserRight='(?<Policy>.*)'.*")
    {
        if ($UserRightsHash.ContainsKey($Matches.Policy))
        {
            $retHash.Policy = $UserRightsHash[$Matches.Policy]
            $retHash.Identity = $TempValue -split ","
        }
        else
        {
            Write-Error "Cannot find matching User Right for Privilege ($($Matches.Privilege))"
            return ""
        }
    }
    else
    {
        Write-Error "Privilege String is not formatted correctly ($($DiscoveryData.WMIDiscoveryInfo.Where))"
        return ""
    }

    Write-DSCString -Resource -Name $Name -Type UserRightsAssignment -Parameters $retHash -Comment $Comments
}

Function Write-XMLScriptData
{
    [CmdletBinding()]
    [OutputType([hashtable])]
    param
    (
        [Parameter(Mandatory=$true)]   
        [System.Xml.XmlElement]$DiscoveryData,
        
        [Parameter(Mandatory=$true)]   
        [System.Xml.XmlElement]$ValueData
    )

    $retHash = @{}
    

    return ""
}

Function Write-XMLWMIData
{
    [CmdletBinding()]
    [OutputType([hashtable])]
    param
    (
        [Parameter(Mandatory=$true)]   
        [System.Xml.XmlElement]$DiscoveryData,
        
        [Parameter(Mandatory=$true)]   
        [System.Xml.XmlElement]$ValueData
    )
    
    $Comments = Get-NodeComments -Node $DiscoveryData
    $Name = $DiscoveryData.SelectNodes("../..").Name
    $Name = "$((Get-NodeDataFromComments -Comments $Comments).'CCEID-50'): $Name"

    # Grab the ExistensialRule and Validation Rule.
    $ExistensialRule = $valueData.SelectNodes("..").ExistentialRule
    $ValidationRules = $valueData.SelectNodes("..").ValidationRules
       
    $Comments = Get-NodeComments -Node $DiscoveryData
    $Name = $DiscoveryData.SelectNodes("../..").Name
    $Name = "$((Get-NodeDataFromComments -Comments $Comments).'CCEID-50'): $Name"
         
    # Grab the Value and Operator
    $TempValue = $ValidationRules.SettingRule.Value.ValueA
    
    $parseValue = $false
    if ([bool]::TryParse($TempValue, [ref]$parseValue))
    {
        [int]$TempValue = [bool]$parseValue
    }

    $Operator = $ValidationRules.SettingRule.Operator

    $retHash = @{}
    $Where = switch ($DiscoveryData.WMIDiscoveryInfo.Where) { {$_."#text"} {$_."#text"} Default { $_ } }
    $KeyName = ""
    if ($Where -match "KeyName.*'(?<Name>[A-Z]*)'.*")
    {
        $KeyName = $Matches.Name
    }
    else
    {
        Write-Error "Cannot extract Name from $Where"
        return ""
    }
    
    Write-DSCString -Resource -Name $Name -Type SecuritySetting -Parameters @{$KeyName = $TempValue;Name = $KeyName} -Comment $Comments
}

Function Write-JSONRegistryData
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory = $true)]
        $RegistryData
    )

    $ValueData = -1
    switch ($RegistryData.RegValueType)
    {
        "Int" 
        {
            if (!([int]::TryParse($RegistryData.ExpectedValue, [ref]$ValueData)))
            {
                Write-Warning "Could not parse Policy ($($RegistryData.Name)) with ExpectedValue ($($RegistryData.ExpectedValue)) as ($($RegistryData.RegValueType))"
                continue
            }
            else
            {
                $ValueType = "DWORD"
            }
        }

        "String"
        {
            $ValueData = $RegistryData.ExpectedValue.ToString()
            $ValueType = "String"
        }

        "MultipleString"
        {
            $ValueData = $RegistryData.ExpectedValue.ToString()
            $ValueType = "MultiString"
        }
    }

    switch ($RegistryData.Hive)
    {
        "LocalMachine" { $RegistryData.Hive = "HKLM:" }
    }
    
    if ($ValueType -eq "DWORD" -and ($ValueData -match "(Disabled|Enabled|Not Defined|True|False)" -or $ValueData -eq "''"))
    {
        # This is supposed to be an INT and it's a String
        [int]$Value = @{"Disabled"=0;"Enabled"=1;"Not Defined"=0;"True"=1;"False"=0;''=0}.$Value
    }

    $policyHash = @{}
    $policyHash.Key = $([string]$RegistryData.Hive, [string]$RegistryData.KeyPath -join "\" )
    $policyHash.ValueName = $RegistryData.ValueName
    $policyHash.ValueType = $ValueType
    $policyHash.ValueData = $ValueData
    
    if ($policyHash.ValueType -eq "None")
    {
        # The REG_NONE is not allowed by the Registry resource.
        $policyHash.Remove("ValueType")
    }

    if ([string]::IsNullOrEmpty($policyHash.ValueName))
    {
        $policyHash.Remove("ValueData")
    }

    $commentOUT = $false                
    If ([string]::IsNullOrEmpty($RegistryData.KeyPath))
    {
        $CommentOUT = $true
    }
             
    return Write-DSCString -Resource -Type Registry -Name "$($RegistryData.CCEID): $($RegistryData.Name)" -Parameters $policyHash -CommentOUT:($RegistryData.State -ne 'Enabled')
}

Function Write-JSONAuditData
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory = $true)]
        $AuditData
    )

    if (!$AuditCategoryHash.ContainsKey($AuditData.AuditPolicyID))
    {
        Write-Warning "Cannot find Audit Policy for ($($AuditData.AuditPolicyID)) from ($($AuditData.Name))"
        return ""
    }
    else
    {
        $Category = $AuditCategoryHash[$AuditData.AuditPolicyID]
        $Ensure = "Absent", "Present"
        $AuditData.ExpectedValue = ($AuditData.ExpectedValue -replace "Success And Failure", "SuccessAndFailure") -replace "No Auditing", "NoAuditing"
            
        $policyHash = @{}
        $policyHash.AuditFlag = $AuditData.ExpectedValue
        $policyHash.Name = $Category 
            
        switch ($policyHash.AuditFlag)
        {
            "SuccessAndFailure" 
            {
                $policyHash.AuditFlag = "Success"
                Write-DSCString -Resource -Type AuditPolicySubCategory -Name "$($AuditData.CCEID): $($AuditData.Name) (Success)" -Parameters $policyHash -CommentOUT:($AuditData.State -ne 'Enabled')
                $policyHash.AuditFlag = "Failure"
                Write-DSCString -Resource -Type AuditPolicySubCategory -Name "$($AuditData.CCEID): $($AuditData.Name) (Failure)" -Parameters $policyHash -CommentOUT:($AuditData.State -ne 'Enabled')
            }
            
            "NoAuditing" 
            {
                $policyHash.Ensure = "Absent"
                $policyHash.AuditFlag = "Success"
                Write-DSCString -Resource -Type AuditPolicySubcategory -Name "$($AuditData.CCEID): $($AuditData.Name) (Success)" -Parameters $policyHash -CommentOUT:($AuditData.State -ne 'Enabled')
                $policyHash.AuditFlag = "Failure"
                Write-DSCString -Resource -Type AuditPolicySubcategory -Name "$($AuditData.CCEID): $($AuditData.Name) (Failure)" -Parameters $policyHash -CommentOUT:($AuditData.State -ne 'Enabled')
            } 

            Default
            {
                Write-DSCString -Resource -Type AuditPolicySubcategory -Name "$($AuditData.CCEID): $($AuditData.Name)" -Parameters $policyHash -CommentOUT:($AuditData.State -ne 'Enabled')
            }
        }
    }
}

Function Write-JSONPrivilegeData
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory = $true)]
        $PrivilegeData
    )

    $Privilege = $PrivilegeData.SettingName
    if ($UserRightsHash.ContainsKey($Privilege))
    {
        $Privilege = $UserRightsHash[$PrivilegeData.SettingName]
    }
    else
    {
        Write-Error "Cannot find privilege $Privilege"
        return ""
    }

    $Accounts = @()
    switch (($PrivilegeData.ExpectedValue -split ", "))
    {
        "No One" { $Accounts = ""; break }
        "SERVICE" { $Accounts += "NT AUTHORITY\SERVICE" } 
        "NEW_VALUE" { }
        "LOCAL SERVICE" { $Accounts += "NT AUTHORITY\LOCAL SERVICE" }
        "AUTHENTICATED USERS" { $Accounts += "NT AUTHORITY\AUTHENTICATED USERS" }
        "Administrators" { $Accounts += "BUILTIN\Administrators" }
        "NETWORK SERVICE" { $Accounts += "NT AUTHORITY\NETWORK SERVICE" }
        "NT AUTHORITY\Local account and member of Administrators group" { $Accounts += "[Local Account|Administrator]" }
        "NT AUTHORITY\Local account" { $Accounts += "[Local Account]"}
        "Guests" { $Accounts += "BUILTIN\Guests"}
        Default { Write-Warning "Found a new Account Value for JSONPrivilege: $_" }
    }
                                
    $policyHash = @{}
    if ([string]::IsNullOrEmpty($Accounts))
    {
        $policyHash.Force = $true
    }    
    
    $policyHash.Policy = $Privilege
    $policyHash.Identity = $Accounts                    
                    
    return Write-DSCString -Resource -Name "$($PrivilegeData.CCEID): $($PrivilegeData.Name)" -Type UserRightsAssignment -Parameters $policyHash -CommentOUT:($PrivilegeData.State -ne 'Enabled')
}

# Clear our processing history on each Configuration Creation.
Function Clear-ProcessingHistory
{
    $ProcessingHistory.Clear()
}

# Write out summary data and output proper files based on success/failure.
Function Write-ProcessingHistory
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [bool]$Pass,

        [Parameter()]
        [string]$OutputPath = $(Join-Path -Path $pwd.path -ChildPath "Output")
    )
    
    if (!(Test-Path $OutputPath))
    {
        mkdir $OutputPath | Out-Null
    }

    if (((Get-Module -ListAvailable).name -contains "Pester"))
    {
        Import-Module Pester
    }
    elseif (!((Get-Module).name -contains "Pester"))
    {
        Write-ProcessingHistory_NonPester -Pass $Pass 
        Write-ProcessingHistory_NonPester -Pass $Pass *> $(Join-Path -Path $OutputPath -ChildPath "Summary.log")
        return
    }
        
    $Tests = @()
    New-Variable -Name successes -Option AllScope -Force
    New-Variable -Name disabled -Option AllScope -Force
    New-Variable -Name conflict -Option AllScope -Force
    $successes = $disabled = $conflict = 0
    $History = Get-Variable ProcessingHistory
    foreach($KeyPair in $History.Value.GetEnumerator())
    {
        $old_success = $successes
        $old_disabled = $disabled
        $old_conflict = $conflict        
        $Describe = "Parsing Summary: $((Get-PsCallStack)[1].Command) - $(@("FAILED", "SUCCEEDED")[[int]$Pass])`n`t$($KeyPair.Key.ToUpper()) Resources"        
        Describe $Describe {

            foreach ($Resource in $KeyPair.Value.Where({($_.Disabled -eq $false) -and ($_.Conflict -eq $false)}))
            {
                It "Parsed: $($Resource.Name)" {
                    $Resource.Disabled | Should Be $false
                } 
                $successes++                         
            }

            foreach ($Resource in $KeyPair.Value.Where({$_.Disabled}))
            {
                It "Disabled: $($Resource.Name)" {
                    $Resource.Disabled | Should Be $true
                }          
                $disabled++
            }

            foreach ($Resource in $KeyPair.Value.Where({$_.Conflict}))
            {
                It "Found Conflicts: $($Resource.Name)" {
                    $Resource.Conflict | Should Be $true
                }          
                $conflict++
            }
        } *>> $(Join-Path -Path $OutputPath -ChildPath "Summary.log")

        $successes = $old_success
        $disabled = $old_disabled
        $conflict = $old_conflict

        Describe $Describe {

            foreach ($Resource in $KeyPair.Value.Where({($_.Disabled -eq $false) -and ($_.Conflict -eq $false)}))
            {
                It "Parsed: $($Resource.Name)" {
                    $Resource.Disabled | Should Be $false
                } 
                $successes++                         
            }

            foreach ($Resource in $KeyPair.Value.Where({$_.Disabled}))
            {
                It "Disabled: $($Resource.Name)" {
                    $Resource.Disabled | Should Be $true
                }          
                $disabled++
            }

            foreach ($Resource in $KeyPair.Value.Where({$_.Conflict}))
            {
                It "Found Conflicts: $($Resource.Name)" {
                    $Resource.Conflict | Should Be $true
                }          
                $conflict++
            }
        } 
    } 
    
    $tmpBlock = { 
        Write-Host "TOTALS" -ForegroundColor White 
        Write-Host "------" -ForegroundColor White
        Write-Host "SUCCESSES: $successes" -ForegroundColor Green
        Write-Host "DISABLED: $disabled" -ForegroundColor Gray
        Write-Host "CONFLICTS: $conflict" -ForegroundColor Red
        Write-Host "______________________" -ForegroundColor White
        Write-Host "TOTAL: $($successes + $disabled + $conflict)" -ForegroundColor White
    }

    $tmpBlock.Invoke() *>> $(Join-Path -Path $OutputPath -ChildPath "Summary.log")
    $tmpBlock.Invoke()
}

Function Write-ProcessingHistory_NonPester
{
    [CmdletBinding()]
    [OutputType([String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [bool]$Pass
    )
        
    Write-Host "Parsing Summary: $((Get-PsCallStack)[1].Command) - $(@("FAILED", "SUCCEEDED")[[int]$Pass])" -ForegroundColor @("RED", "GREEN")[[int]$Pass]
    Write-Host "---------------" -ForegroundColor White
    $History = Get-Variable ProcessingHistory
    $successes = $disabled = $conflict = 0
    foreach($KeyPair in $History.Value.GetEnumerator())
    {
        foreach ($Resource in $KeyPair.Value.Where({($_.Disabled -eq $false) -and ($_.Conflict -eq $false)}))
        {
            Write-Host "Parsed: $($Resource.Name)" -ForegroundColor Green
            $successes++
        }

        foreach ($Resource in $KeyPair.Value.Where({$_.Disabled}))
        {
            Write-Host "Disabled: $($Resource.Name)" -ForegroundColor Gray
            $disabled++          
        }

        foreach ($Resource in $KeyPair.Value.Where({$_.Conflict}))
        {
            Write-Host "Found Conflicts: $($Resource.Name)" -ForegroundColor Red
            $conflict++      
        }
    }

    Write-Host "TOTALS" -ForegroundColor White
    Write-Host "------" -ForegroundColor White
    Write-Host "SUCCESSES: $successes" -ForegroundColor Green
    Write-Host "DISABLED: $disabled" -ForegroundColor Gray
    Write-Host "CONFLICTS: $conflict" -ForegroundColor Red
    Write-Host "______________________" -ForegroundColor White
    Write-Host "TOTAL: $($successes + $disabled + $conflict)" -ForegroundColor White
}


Export-ModuleMember -Cmdlet * -Function * -Variable *