DSCResources/AuditPolicyResourceHelper/AuditPolicyResourceHelper.psm1
#Requires -Version 4.0 <# This PS module contains functions for Desired State Configuration (DSC) AuditPolicyDsc provider. It enables querying, creation, removal and update of Windows advanced audit policies through Get, Set, and Test operations on DSC managed nodes. #> <# .SYNOPSIS Retrieves the localized string data based on the machine's culture. Falls back to en-US strings if the machine's culture is not supported. .PARAMETER ResourceName The name of the resource as it appears before '.strings.psd1' of the localized string file. For example: AuditPolicySubcategory: MSFT_AuditPolicySubcategory AuditPolicyOption: MSFT_AuditPolicyOption #> function Get-LocalizedData { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ParameterSetName = 'resource')] [ValidateNotNullOrEmpty()] [String] $ResourceName, [Parameter(Mandatory = $true, ParameterSetName = 'helper')] [ValidateNotNullOrEmpty()] [String] $HelperName ) # With the helper module just update the name and path variables as if it were a resource. if ($PSCmdlet.ParameterSetName -eq 'helper') { $resourceDirectory = $PSScriptRoot $ResourceName = $HelperName } else { # Step up one additional level to build the correct path to the resource culture. $resourceDirectory = Join-Path -Path ( Split-Path $PSScriptRoot -Parent ) ` -ChildPath $ResourceName } $localizedStringFileLocation = Join-Path -Path $resourceDirectory -ChildPath $PSUICulture if (-not (Test-Path -Path $localizedStringFileLocation)) { # Fallback to en-US $localizedStringFileLocation = Join-Path -Path $resourceDirectory -ChildPath 'en-US' } Import-LocalizedData ` -BindingVariable 'localizedData' ` -FileName "$ResourceName.strings.psd1" ` -BaseDirectory $localizedStringFileLocation return $localizedData } <# .SYNOPSIS Invoke-AuditPol is a private function that wraps auditpol.exe providing a centralized function to manage access to and the output of auditpol.exe. .DESCRIPTION The function will accept a string to pass to auditpol.exe for execution. Any 'get' or 'set' opertions can be passed to the central wrapper to execute. All of the nuances of auditpol.exe can be further broken out into specalized functions that call Invoke-AuditPol. Since the call operator is being used to run auditpol, the input is restricted to only execute against auditpol.exe. Any input that is an invalid flag or parameter in auditpol.exe will return an error to prevent abuse of the call. The call operator will not parse the parameters, so they are split in the function. .PARAMETER Command The action that audtipol should take on the subcommand. .PARAMETER SubCommand The subcommand to execute. .OUTPUTS The raw string output of auditpol.exe with the /r switch to return a CSV string. .EXAMPLE Invoke-AuditPol -Command 'Get' -SubCommand 'Subcategory:Logon' #> function Invoke-AuditPol { [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateSet('Get', 'Set', 'List')] [System.String] $Command, [Parameter(Mandatory = $true)] [System.String[]] $SubCommand ) # Localized messages for Write-Verbose statements in this resource $localizedData = Get-LocalizedData -HelperName 'AuditPolicyResourceHelper' <# The raw auditpol data with the /r switch is a 3 line CSV 0 - header row 1 - blank row 2 - the data row we are interested in #> # set the base commands to execute if ( $Command -eq 'Get') { $commandString = @("/$Command","/$SubCommand","/r" ) } else { # the set subcommand comes in an array of the subcategory and flag $commandString = @("/$Command","/$($SubCommand[0])",$SubCommand[1] ) } Write-Debug -Message ( $localizedData.ExecuteAuditpolCommand -f $commandString ) try { # Use the call operator to process the auditpol command $return = & "auditpol.exe" $commandString 2>&1 # auditpol does not throw exceptions, so test the results and throw if needed if ( $LASTEXITCODE -ne 0 ) { throw New-Object System.ArgumentException } $return } catch [System.Management.Automation.CommandNotFoundException] { # catch error if the auditpol command is not found on the system Write-Error -Message $localizedData.AuditpolNotFound } catch [System.ArgumentException] { # catch the error thrown if the lastexitcode is not 0 [String] $errorString = $error[0].Exception $errorString = $errorString + "`$LASTEXITCODE = $LASTEXITCODE;" $errorString = $errorString + " Command = auditpol $commandString" Write-Error -Message $errorString } catch { # catch any other errors Write-Error -Message ( $localizedData.UnknownError -f $error[0] ) } } <# .SYNOPSIS Returns the list of valid Subcategories. .DESCRIPTION This funciton will check if the list of valid subcategories has already been created. If the list exists it will simply return it. If it doe not exists, it will generate it and return it. #> function Get-ValidSubcategoryList { [OutputType([String[]])] [CmdletBinding()] param () if ($null -eq $script:validSubcategoryList) { $script:validSubcategoryList = @() # Populating $validSubcategoryList uses Invoke-AuditPol and needs to follow the definition. Invoke-AuditPol -Command List -SubCommand "Subcategory:*" | Where-Object { $_ -notlike 'Category/Subcategory*' } | ForEach-Object { # The categories do not have any space in front of them, but the subcategories do. if ( $_ -like " *" ) { $script:validSubcategoryList += $_.trim() } } } return $script:validSubcategoryList } <# .SYNOPSIS Verifies that the Subcategory is valid. .PARAMETER Name The name of the Subcategory to validate. #> function Test-ValidSubcategory { [OutputType([Boolean])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String] $Name ) if ( ( Get-ValidSubcategoryList ) -icontains $Name ) { return $true } else { return $false } } Export-ModuleMember -Function @( 'Invoke-AuditPol', 'Get-LocalizedData', 'Test-ValidSubcategory' ) |