helpers/DscOperations.psm1

Set-StrictMode -Version latest
$ErrorActionPreference = 'Stop'

Import-Module $PSScriptRoot\GuestConfigPath.psm1

$script:ExecuteDscOperationsScript = @"
using System;
using System.Runtime.InteropServices;
using System.Management.Automation;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace GuestConfig
{{
    public class DscOperations
    {{
        [DllImport("{0}", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr new_dsc_library_context(string assignment_name, string dsc_binary_path, IntPtr writeMessageCallback, IntPtr writeErrorCallback, IntPtr writeResultCallback);

        [DllImport("{0}", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
        public static extern Int32 test_dsc_configuration(IntPtr context, string job_id, string assignment_name, string file_path);

        [DllImport("{0}", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
        public static extern Int32 get_dsc_configuration(IntPtr context, string job_id, string assignment_name, string file_path);

        [DllImport("{0}", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
        public static extern Int32 publish_dsc_assignment(IntPtr context, string job_id, string assignment_name, string assignments_path);

        [DllImport("{0}", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
        public static extern Int32 set_dsc_meta_configuration(IntPtr context, string job_id, string assignment_name, string assignments_path);

        [DllImport("{0}", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
        public static extern void delete_dsc_library_context(IntPtr context);

        internal enum MessageChannel
        {{
            Warning,
            Verbose,
            Debug,
            Error
        }}

        public DscOperations()
        {{
            m_messages = new List<Tuple<MessageChannel, string>>();
            m_result = "";

            WriteMessageDelegate delegate_write_message = new WriteMessageDelegate(WriteMessage);
            GCHandle m_write_message_gc_handle = GCHandle.Alloc(delegate_write_message);
            m_write_message_callback = Marshal.GetFunctionPointerForDelegate(delegate_write_message);

            WriteErrorDelegate delegate_write_error = new WriteErrorDelegate(WriteError);
            m_write_error_gc_handle = GCHandle.Alloc(delegate_write_error);
            m_write_error_callback = Marshal.GetFunctionPointerForDelegate(delegate_write_error);

            WriteResultDelegate delegate_write_result = new WriteResultDelegate(WriteResult);
            m_write_result_gc_handle = GCHandle.Alloc(delegate_write_result);
            m_write_result_callback = Marshal.GetFunctionPointerForDelegate(delegate_write_result);
        }}

        ~DscOperations()
        {{
            if (m_write_message_gc_handle.IsAllocated)
            {{
                m_write_message_gc_handle.Free();
            }}

            if (m_write_error_gc_handle.IsAllocated)
            {{
                m_write_error_gc_handle.Free();
            }}

            if (m_write_result_gc_handle.IsAllocated)
            {{
                m_write_result_gc_handle.Free();
            }}
        }}

        public string TestDscConfiguration(PSCmdlet ps_cmdlet, string job_id, string configuration_name, string gc_bin_path)
        {{
            IntPtr context = IntPtr.Zero;
            try
            {{
                ClearMessages();

                context = new_dsc_library_context(configuration_name, gc_bin_path, m_write_message_callback, m_write_error_callback, m_write_result_callback);
                if(context == IntPtr.Zero)
                {{
                    ps_cmdlet.WriteError(CreateErrorRecord("TestGuestConfiguration", "Failed to initialize Guest Configuration library.", true));
                }}

                Int32 result = test_dsc_configuration(context, job_id, configuration_name, "");
                for (int i = 0; i < m_messages.Count; i++)
                {{
                    var message = m_messages[i];
                    if(message.Item1 == MessageChannel.Error)
                    {{
                        ps_cmdlet.WriteError(CreateErrorRecord("TestGuestConfiguration", message.Item2, false));
                    }}
                    else if(message.Item1 == MessageChannel.Warning)
                    {{
                        ps_cmdlet.WriteWarning(message.Item2);
                    }}
                    else if(message.Item1 == MessageChannel.Debug)
                    {{
                        ps_cmdlet.WriteDebug(message.Item2);
                    }}
                    else
                    {{
                        ps_cmdlet.WriteVerbose(message.Item2);
                    }}
                }}
            }}
            finally
            {{
                delete_dsc_library_context(context);
            }}

            return m_result;
        }}

        public string GettDscConfiguration(PSCmdlet ps_cmdlet, string job_id, string configuration_name, string gc_bin_path)
        {{
            IntPtr context = IntPtr.Zero;
            try
            {{
                ClearMessages();

                context = new_dsc_library_context(configuration_name, gc_bin_path, m_write_message_callback, m_write_error_callback, m_write_result_callback);
                if(context == IntPtr.Zero)
                {{
                    ps_cmdlet.WriteError(CreateErrorRecord("TestGuestConfiguration", "Failed to initialize Guest Configuration library.", true));
                }}

                Int32 result = get_dsc_configuration(context, job_id, configuration_name, "");
                for (int i = 0; i < m_messages.Count; i++)
                {{
                    var message = m_messages[i];
                    if(message.Item1 == MessageChannel.Error)
                    {{
                        ps_cmdlet.WriteError(new ErrorRecord(
                                    new InvalidOperationException(message.Item2),
                                    "TestGuestConfiguration",
                                    ErrorCategory.InvalidResult,
                                    null));
                    }}
                    else if(message.Item1 == MessageChannel.Warning)
                    {{
                        ps_cmdlet.WriteWarning(message.Item2);
                    }}
                    else
                    {{
                        ps_cmdlet.WriteVerbose(message.Item2);
                    }}
                }}
            }}
            finally
            {{
                delete_dsc_library_context(context);
            }}

            return m_result;
        }}

        public void PublishDscConfiguration(PSCmdlet ps_cmdlet, string job_id, string configuration_name, string gc_bin_path, string policy_path)
        {{
            IntPtr context = IntPtr.Zero;
            try
            {{
                ClearMessages();

                context = new_dsc_library_context(configuration_name, gc_bin_path, m_write_message_callback, m_write_error_callback, m_write_result_callback);
                if(context == IntPtr.Zero)
                {{
                    ps_cmdlet.WriteError(CreateErrorRecord("TestGuestConfiguration", "Failed to initialize Guest Configuration library.", true));
                }}

                Int32 result = publish_dsc_assignment(context, job_id, configuration_name, policy_path);
                if(result != 0)
                {{
                    ps_cmdlet.WriteError(CreateErrorRecord("TestGuestConfiguration", "Failed to publish Guest Configuration policy package.", true));
                }}
            }}
            finally
            {{
                delete_dsc_library_context(context);
            }}
        }}

        public void SetDscLocalConfigurationManager(PSCmdlet ps_cmdlet, string job_id, string configuration_name, string gc_bin_path, string policy_path)
        {{
            IntPtr context = IntPtr.Zero;
            try
            {{
                ClearMessages();

                context = new_dsc_library_context(configuration_name, gc_bin_path, m_write_message_callback, m_write_error_callback, m_write_result_callback);
                if(context == IntPtr.Zero)
                {{
                    ps_cmdlet.WriteError(CreateErrorRecord("TestGuestConfiguration", "Failed to initialize Guest Configuration library.", true));
                }}

                Int32 result = set_dsc_meta_configuration(context, job_id, configuration_name, policy_path);
                if(result != 0)
                {{
                    ps_cmdlet.WriteError(CreateErrorRecord("TestGuestConfiguration", "Failed to set Meta config settings.", true));
                }}
            }}
            finally
            {{
                delete_dsc_library_context(context);
            }}
        }}

        private delegate Int32 WriteMessageDelegate(Int32 channel, IntPtr message);
        private delegate Int32 WriteErrorDelegate(IntPtr error);
        private delegate Int32 WriteResultDelegate(IntPtr result);

        private string m_result;
        private List<Tuple<MessageChannel, string>> m_messages;

        private GCHandle m_write_message_gc_handle;
        private GCHandle m_write_error_gc_handle;
        private GCHandle m_write_result_gc_handle;
        private IntPtr m_write_message_callback;
        private IntPtr m_write_error_callback;
        private IntPtr m_write_result_callback;

        internal Int32 WriteMessage(Int32 channel, IntPtr message_ptr)
        {{
            string message;
            message = Marshal.PtrToStringAnsi(message_ptr);
            m_messages.Add(Tuple.Create((MessageChannel)channel, message));
            return 0;
        }}

        internal Int32 WriteError(IntPtr error_ptr)
        {{
            string error;
            error = Marshal.PtrToStringAnsi(error_ptr);
            m_messages.Add(Tuple.Create(MessageChannel.Error, error));
            return 0;
        }}

        internal Int32 WriteResult(IntPtr result_ptr)
        {{
            m_result = Marshal.PtrToStringAnsi(result_ptr);
            return 0;
        }}

        private void ClearMessages()
        {{
            m_messages.Clear();
        }}

        private ErrorRecord CreateErrorRecord(string error_id, string error_message, bool include_error_from_message_list)
        {{
            string error = error_message + "\r\n";
            for (int i = 0; include_error_from_message_list && i < m_messages.Count; i++)
            {{
                var message = m_messages[i];
                if(message.Item1 == MessageChannel.Error)
                {{
                    error = message.Item2 + "\r\n";
                }}
            }}

            return new ErrorRecord(
                    new InvalidOperationException(error),
                    error_id,
                    ErrorCategory.InvalidResult,
                    null);
        }}
    }}
}}
"@



<#
    .SYNOPSIS
        Test DSC configuration.

    .Parameter ConfigurationName
        Configuration name.

    .Example
        Test-DscConfiguration -ConfigurationName WindowsTLS
#>


function Test-DscConfiguration
{
    [CmdletBinding()]
    param (
        [parameter(Position=0, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $ConfigurationName
    )

    $job_id = [guid]::NewGuid().Guid
    $gcBinPath = Get-GuestConfigBinaryPath
    $dsclibPath = $(Get-DscLibPath) -replace  '[""\\]','\$&'

    if(-not ([System.Management.Automation.PSTypeName]'GuestConfig.DscOperations').Type) {
        $addTypeScript = $ExecuteDscOperationsScript -f $dsclibPath
        Add-Type -TypeDefinition $addTypeScript -ReferencedAssemblies 'System.Management.Automation','System.Console','System.Collections'
    }

    $dscOperation = [GuestConfig.DscOperations]::New()
    $result = $dscOperation.TestDscConfiguration($PSCmdlet, $job_id, $ConfigurationName, $gcBinPath)

    return ConvertFrom-Json $result
}

<#
    .SYNOPSIS
        Get DSC configuration.

    .Parameter ConfigurationName
        Configuration name.

    .Example
        Get-DscConfiguration -ConfigurationName WindowsTLS
#>


function Get-DscConfiguration
{
    [CmdletBinding()]
    param (
        [parameter(Position=0, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $ConfigurationName
    )

    $job_id = [guid]::NewGuid().Guid
    $gcBinPath = Get-GuestConfigBinaryPath
    $dsclibPath = $(Get-DscLibPath) -replace  '[""\\]','\$&'

    if(-not ([System.Management.Automation.PSTypeName]'GuestConfig.DscOperations').Type) {
        $addTypeScript = $ExecuteDscOperationsScript -f $dsclibPath
        Add-Type -TypeDefinition $addTypeScript -ReferencedAssemblies 'System.Management.Automation','System.Console','System.Collections'
    }

    $dscOperation = [GuestConfig.DscOperations]::New()
    $result = $dscOperation.GettDscConfiguration($PSCmdlet, $job_id, $ConfigurationName, $gcBinPath)

    return ConvertFrom-Json $result
}

<#
    .SYNOPSIS
        Publish DSC configuration.

    .Parameter ConfigurationName
        Configuration name.

    .Example
        Publish-DscConfiguration -Path C:\metaconfig
#>


function Publish-DscConfiguration
{
    [CmdletBinding()]
    param (
        [parameter(Position=0, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $ConfigurationName,

        [parameter(Position=1, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $Path
    )

    $job_id = [guid]::NewGuid().Guid
    $gcBinPath = Get-GuestConfigBinaryPath
    $dsclibPath = $(Get-DscLibPath) -replace  '[""\\]','\$&'

    if(-not ([System.Management.Automation.PSTypeName]'GuestConfig.DscOperations').Type) {
        $addTypeScript = $ExecuteDscOperationsScript -f $dsclibPath
        Add-Type -TypeDefinition $addTypeScript -ReferencedAssemblies 'System.Management.Automation','System.Console','System.Collections'
    }

    $dscOperation = [GuestConfig.DscOperations]::New()
    $result = $dscOperation.PublishDscConfiguration($PSCmdlet, $job_id, $ConfigurationName, $gcBinPath, $Path)
}

<#
    .SYNOPSIS
        Set DSC LCM settings.

    .Parameter ConfigurationName
        Configuration name.

    .Example
        Set-DscLocalConfigurationManager -Path C:\metaconfig
#>


function Set-DscLocalConfigurationManager
{
    [CmdletBinding()]
    param (
        [parameter(Position=0, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $ConfigurationName,

        [parameter(Position=1, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $Path
    )

    $job_id = [guid]::NewGuid().Guid
    $gcBinPath = Get-GuestConfigBinaryPath
    $dsclibPath = $(Get-DscLibPath) -replace  '[""\\]','\$&'

    if(-not ([System.Management.Automation.PSTypeName]'GuestConfig.DscOperations').Type) {
        $addTypeScript = $ExecuteDscOperationsScript -f $dsclibPath
        Add-Type -TypeDefinition $addTypeScript -ReferencedAssemblies 'System.Management.Automation','System.Console','System.Collections'
    }

    $dscOperation = [GuestConfig.DscOperations]::New()
    $result = $dscOperation.SetDscLocalConfigurationManager($PSCmdlet, $job_id, $ConfigurationName, $gcBinPath, $Path)
}