DSCResources/VE_ROABuildingBlock/VE_ROABuildingBlock.psm1

data localizedData {
    # Localized messages; culture="en-US"
    ConvertFrom-StringData @'
        CannotFindPathError = Cannot find path '{0}' because it does not exist.
 
        ResourceCorrectPropertyState = Resource property '{0}' is in the desired state.
        ResourceIncorrectPropertyState = Resource property '{0}' is NOT in the desired state. Expected '{1}', actual '{2}'.
        ResourceInDesiredState = Resource '{0}' is in the desired state.
        ResourceNotInDesiredState = Resource '{0}' is NOT in the desired state.
        ImportingBuildingBlock = Importing building block '{0}'.
'@

}

#region Private

function SetBuildingBlockFileHash {
<#
    .SYNOPSIS
        Updates the registry with friendly names and hash values.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [System.String] $RegistryName,

        [Parameter(Mandatory)]
        [System.String] $FileHash
    )
    process {

        if (-not (Test-Path -Path $script:DefaultRegistryPath -PathType Container)) {
            $registryParentPath = Split-Path -Path $script:DefaultRegistryPath -Parent;
            $registryKeyName = Split-Path -Path $script:DefaultRegistryPath -Leaf;
            [ref] $null = New-Item -Path $registryParentPath -ItemType Directory -Name $registryKeyName;
        }

        [ref] $null = Set-ItemProperty -Path $script:DefaultRegistryPath -Name $RegistryName -Value $FileHash;

    } #end process
} #end function SetBuildingBlockFileHash


function ResolveBuildingBlock {
<#
    .SYNOPSIS
        Returns a list of resolved RES ONE Automation building block files/hashes.
#>

    [CmdletBinding()]
    [OutputType([System.Management.Automation.PSCustomObject])]
    param (
        ## File path containing RES ONE Automation Manager building blocks.
        [Parameter(Mandatory)]
        [System.String] $Path
    )
    process {

        $paths = @()
        foreach ($filePath in $Path) {

            if (-not (Test-Path -Path $filePath)) {

                $exMessage = $localizedData.CannotFindPathError -f $filePath;
                $ex = New-Object System.Management.Automation.ItemNotFoundException $exMessage;
                $category = [System.Management.Automation.ErrorCategory]::ObjectNotFound;
                $errRecord = New-Object System.Management.Automation.ErrorRecord $ex, 'PathNotFound', $category, $filePath;
                $psCmdlet.WriteError($errRecord);
                continue;
            }

            # Resolve any wildcards that might be in the path
            $provider = $null;
            $paths += $psCmdlet.SessionState.Path.GetResolvedProviderPathFromPSPath($filePath, [ref] $provider);

        }

        foreach ($filePath in $paths) {
            Write-Output -InputObject ([PSCustomObject] @{
                Path = $filePath;
                FileHash = (Get-FileHash -Path $filePath -Algorithm MD5).Hash;
                RegistryName = (Split-Path -Path $filePath -Leaf).Replace(' ','').Replace('-','').Replace('.','_');
            });
        }

    } #end process
} #end function ResolveBuildingBlock

#endregion Private

function Get-TargetResource {
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param (
        ## RES ONE Automation database server name/instance (equivalient to DBSERVER).
        [Parameter(Mandatory)]
        [System.String] $Path,

        ## RES ONE Automation authentication credential.
        [Parameter(Mandatory)]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()] $Credential,

        ## Credential supplied is an internal RES ONE Automation user.
        [Parameter()]
        [System.Boolean] $IsRESONEAutomationCredential
    )
    process {

        $buildingBlocks = ResolveBuildingBlock -Path $Path;
        foreach ($bb in $buildingBlocks) {
            Write-Output -InputObject @{ Path = $bb.Path; }
        }

    }
} #end function Get-TargetResource


function Test-TargetResource {
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param (
        [Parameter(Mandatory)]
        [System.String] $Path,

        ## RES ONE Automation authentication credential.
        [Parameter(Mandatory)]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()] $Credential,

        [Parameter()]
        [System.Boolean] $IsRESONEAutomationCredential
    )
    process {

        $inCompliance = $true;
        $buildingBlocks = ResolveBuildingBlock -Path $Path;

        foreach ($bb in $buildingBlocks) {

            $registryName = $bb.RegistryName;
            $registryHash = (Get-ItemProperty -Path $script:DefaultRegistryPath -Name $bb.RegistryName -ErrorAction SilentlyContinue).$RegistryName;
            if ($bb.FileHash -ne $registryHash) {

                Write-Verbose -Message ($localizedData.ResourceIncorrectPropertyState -f $bb.RegistryName, $bb.FileHash, $registryHash);
                $inCompliance = $false;
            }
            else {

                Write-Verbose -Message ($localizedData.ResourceCorrectPropertyState -f $bb.RegistryName);
            }

        }

        if ($inCompliance) {

            Write-Verbose -Message ($localizedData.ResourceInDesiredState -f $Path);
            return $true;
        }
        else {

            Write-Verbose -Message ($localizedData.ResourceNotInDesiredState -f $Path);
            return $false;
        }

    }
} #end function Test-TargetResource


function Set-TargetResource {
    [CmdletBinding()]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    param (
        [Parameter(Mandatory)]
        [System.String] $Path,

        ## RES ONE Automation authentication credential.
        [Parameter(Mandatory)]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()] $Credential,

        [Parameter()]
        [System.Boolean] $IsRESONEAutomationCredential
    )
    process {

        $buildingBlocks = ResolveBuildingBlock -Path $Path;

        foreach ($bb in $buildingBlocks) {

            $registryName = $bb.RegistryName;
            $registryHash = (Get-ItemProperty -Path $script:DefaultRegistryPath -Name $bb.RegistryName -ErrorAction SilentlyContinue).$registryName;
            if ($bb.FileHash -ne $registryHash) {

                try {

                    ## Import the building block
                    $PSBoundParameters['Path'] = $bb.Path;
                    Write-Verbose -Message ($localizedData.ImportingBuildingBlock -f $bb.Path);
                    Import-ROABuildingBlockFile @PSBoundParameters;

                    ## Update the registry/hash value
                    SetBuildingBlockFileHash -RegistryName $bb.RegistryName -FileHash $bb.FileHash;
                }
                catch {

                    throw $_
                }
            }

        } #end foreach building block

    } #end process
} #end function Set-TargetResource


## Import the ROACommon library functions
$moduleRoot = Split-Path -Path $MyInvocation.MyCommand.Path -Parent;
$moduleParent = Split-Path -Path $moduleRoot -Parent;
Import-Module (Join-Path -Path $moduleParent -ChildPath 'ROACommon') -Force;

$script:DefaultRegistryPath = 'HKLM:\SOFTWARE\Virtual Engine\RESONEAutomationDsc';

Export-ModuleMember -Function *-TargetResource;