Use-Config.ps1

function Use-Config
{
    <#
    .Synopsis
        Applies a saved configuration
    .Description
        Applies a saved DSC configuration to the local machine, a remote machine, or an azure virtual machine
    .Example
        Use-Config MySavedConfig
    .Example
        Use-Config MySavedConfig -AzureVirtualMachine myAzureVM
    .Link
        Save-Config
    .Link
        Get-Config
    #>

    [OutputType([PSObject])]
    param(
    # The name of the configuration
    [Parameter(Mandatory=$true,ParameterSetName='ByName',Position=0, ValueFromPipelineByPropertyName=$true)]
    [String]
    $Name,

    # The parameters to the configuration
    [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)]
    [Hashtable]
    $Parameter = @{},


    # The name of a computer within your network
    [string]
    $ComputerName,

    # The credential used to run the DSC configuration
    [Management.Automation.PSCredential]
    $Credential,

    # The path to a DSC certificate
    [Alias('CertificateFile')]
    [string]
    $CertificatePath,


    # The name of an Azure Virtual Machine. Using this requires that you have the Azure PowerShell module installed.
    #|Options $aModule = Import-Module Azure -Global -Passthru -ErrorAction SilentlyContinue; if ($aModule) { Get-AzureVM | Select-Object -ExpandProperty Name }
    #|Multisele
    [Alias('AzureVM')]
    [string[]]
    $AzureVirtualMachine
    )

    process {
        $theCfg = Get-Config -Name $Name

        if (-not $theCfg) { 
            $foundConfig = $ExecutionContext.SessionState.InvokeCommand.GetCommand("$Name", "Configuration")
            if ($foundConfig) {
                Save-Config -Name $foundConfig -Force
            } else {
                return 
            }            
        } 


        $progId = Get-Random

        if ($AzureVirtualMachine) {
            #region Azure Virutal Machine DSC Resource Use
            $azureModule = Import-Module Azure -Global -PassThru

            if (-not $azureModule) { return } 
            

            Write-Verbose "Publish Started $([DateTime]::Now)"
            Write-Progress "Publishing Configuration" $theCfg.Path -Id $progId

            Publish-AzureVMDscConfiguration -ConfigurationPath $theCfg.Path -Force


            $vmcount = $AzureVirtualMachine.Length

            $startTime = [DateTime]::Now

            $progress = @{Id=$progId}

            $vmCount = 0
            foreach ($VMName in $AzureVirtualMachine) {
                Write-Progress "Getting VM" $VMName @Progress
                if ($AzureVirtualMachine.Length -gt 1) {
                    $progress.PercentComplete = $vmcount * 100 / $AzureVirtualMachine.Length
                }
                $vm = Get-AzureVM $VMName 

                $Splat = @{
                    ConfigurationName=$theCfg.Name
                    ConfigurationArchive = "$($theCfg.Name).config.ps1.zip"
                    VM = $vm
                }

                if ($Parameter -and $Parameter.Count) {
                    $splat.ConfigurationArgument = $Parameter
                }
            
                Write-Progress "Setting DSC Extension" $VMName @Progress
                $setDsc = Set-AzureVMDscExtension @splat
                if ($VerbosePreference -ne 'silentlycontinue') {
                    Write-Verbose "DSC Set Result: $($setDsc | Out-String)"
                    Write-Verbose "VM Updated $([DateTime]::Now)"
                }
                

                Write-Progress "Updating VM" $VMName @Progress
                $updateResult = $vm | Update-AzureVM
                if ($VerbosePreference -ne 'silentlycontinue') {
                    Write-Verbose "DSC Update Result: $($updateResult | Out-String)"
                }

                $vmcount++

            }
            $progress.Remove('PercentComplete')

                       
            $vmStatus = $null
            $vmFinishedCount = 0 
                        
            do {
                if ($vmStatus) {
                    Start-Sleep -Seconds 1 
                }

                foreach ($vmName in $AzureVirtualMachine) {
                    $v = Get-AzureVM $vmName
                    $vmStatus = Get-AzureVMDscExtensionStatus -VM $v -WarningAction SilentlyContinue 
                    if ($vmStatus -and $vmStatus.Timestamp -gt $startTime) {
                        Write-Progress "$($vmStatus.Status) " "$($vmStatus.StatusMessage) " @Progress
                    } else {
                        Write-Progress "Waiting for DSC" " " @Progress
                    }

                    if ($vmStatus.Timestamp -gt $startTime -and 
                        'Success', 'Error' -contains $vmStatus.Status) {
                        $vmFinishedCount++   
                        $vmStatus
                    }
                }
                
            } while ($vmFinishedCount -ne $AzureVirtualMachine.Length)

            Write-Verbose "DSC Complete on VM $([DateTime]::Now)"

            Write-Progress "$($vmStatus.Status) " "$($vmStatus.StatusMessage) " @Progress -Completed                        
            
            #endregion Azure Virutal Machine DSC Resource Use
            return 
        }

        . $theCfg

        $theRealCfg = $ExecutionContext.SessionState.InvokeCommand.GetCommand($Name, 'Configuration')

        $dataPath = Join-Path (Join-Path $env:ProgramData "Start-Automating") "cFg"        

        $Splat = $Parameter + @{            
            OutputPath = "$(Join-Path $dataPath "$Name--$([DateTime]::Now.ToString('o').Replace(':','-'))")"
        }

        if ($CertificatePath) {
            $rp = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($CertificatePath)
            if (-not $rp) { return } 
            $splat.ConfigurationData = @{
                AllNodes = @(
                    @{
                        NodeName = '*'
                        PSDscAllowPlainTextPassword = $True             
                        CertificateFile = "$rp"
                    }
                    @{
                        NodeName = if ($ComputerName) { $ComputerName } else { 'localhost' }
                    }

                )
            }   
        }

        $cfgResult = & $theRealCfg @splat

        if ($cfgResult -and "$($cfgResult.Directory)") {
            
            $splat2 = @{
                Path = $cfgResult.Directory
            }
            
            if ($ComputerName) {
                $splat2.ComputerName = $splat2
            }
            if ($credential) {
                $splat2.Credential = $Credential
            }
            Start-DscConfiguration @Splat2 -Wait -Force                                     
        }
            
        
    }
}