Public/Invoke-LabRecipe.ps1

function Invoke-LabRecipe
{
    [CmdletBinding(SupportsShouldProcess)]
    param 
    (
        [Parameter(Mandatory, ValueFromPipelineByPropertyName, ValueFromPipeline, ParameterSetName = 'ByName')]
        [string]
        $Name,

        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByRecipe')]
        [object]
        $Recipe,

        [Parameter()]
        [ValidateSet('HyperV', 'Azure', 'VMWare')]
        [string]
        $DefaultVirtualizationEngine,

        [Parameter()]
        [pscredential]
        $LabCredential,

        [Parameter()]
        [AutomatedLab.OperatingSystem]
        $DefaultOperatingSystem,

        [Parameter()]
        [AutomatedLab.IpNetwork]
        $DefaultAddressSpace,

        [Parameter()]
        [string]
        $DefaultDomainName,

        [Parameter()]
        [string]
        $OutFile,

        [Parameter()]
        [switch]
        $PassThru,

        [Parameter()]
        [switch]
        $NoDeploy
    )

    process
    {
        if ($Name)
        {
            $Recipe = Get-LabRecipe -Name $Name
        }

        $Recipe.DefaultVirtualizationEngine = if ($DefaultVirtualizationEngine) {$DefaultVirtualizationEngine} elseif ($null -ne $Recipe.DefaultVirtualizationEngine) {$Recipe.DefaultVirtualizationEngine} else {'HyperV'}
        $Recipe.DefaultDomainName = if ($DefaultDomainName) {$DefaultDomainName} elseif ($null -ne $Recipe.DefaultDomainName) {$Recipe.DefaultDomainName} else {'contoso.com'}
        $Recipe.DefaultAddressSpace = if ($DefaultAddressSpace) {$DefaultAddressSpace.ToString()} elseif ($null -ne $Recipe.DefaultAddressSpace) {$Recipe.DefaultAddressSpace} else {(Get-LabAvailableAddresseSpace).ToString()}
        $Recipe.DefaultOperatingSystem = if ($DefaultOperatingSystem) {$DefaultOperatingSystem.OperatingSystemName} elseif ($null -ne $Recipe.DefaultOperatingSystem) {$Recipe.DefaultOperatingSystem} else {'Windows Server 2016 Datacenter'}
        $Recipe.VmPrefix = if ($VmPrefix) {$VmPrefix} elseif ($null -ne $Recipe.VmPrefix) {$Recipe.VmPrefix} else {(1..4 | ForEach-Object { [char[]](65..90) | Get-Random }) -join ''}

        $scriptContent = [System.Text.StringBuilder]::new()
        $null = $scriptContent.AppendLine("New-LabDefinition -Name $($Recipe.Name) -DefaultVirtualizationEngine $($Recipe.DefaultVirtualizationEngine)")
        $null = $scriptContent.AppendLine("Add-LabVirtualNetworkDefinition -Name $($Recipe.Name) -AddressSpace $($Recipe.DefaultAddressSpace)")
        $null = $scriptContent.AppendLine("`$PSDefaultParameterValues.Clear()")
        $null = $scriptContent.AppendLine("`$PSDefaultParameterValues.Add('Add-LabMachineDefinition:Network', '$($Recipe.Name)')")
        $null = $scriptContent.AppendLine("`$PSDefaultParameterValues.Add('Add-LabMachineDefinition:OperatingSystem', '$($Recipe.DefaultOperatingSystem)')")

        foreach ($requiredIso in $Recipe.RequiredProductIsos)
        {
            switch ($requiredIso)
            {
                'CI_CD' {$isoPattern = 'team_foundation'; $isoName = 'Tfs2017'}
                'SQL' {$isoPattern = 'sql_server_2017'; $isoName = 'SQLServer2017'}
            }
            
            $isoFile = Get-ChildItem -Path "$(Get-LabSourcesLocationInternal -Local)\ISOs\*$isoPattern*" | Sort-Object -Property CreationTime | Select-Object -Last 1 -ExpandProperty FullName
            if (-not $isoFile)
            {
                $isoFile = Read-Host -Prompt "Please provide the full path to an ISO for $isoName"
            }

            $null = $scriptContent.AppendLine("Add-LabIsoImageDefinition -Name $isoName -Path $isoFile")
        }

        if (-not $Credential)
        {
            $Credential = New-Object -TypeName pscredential -ArgumentList 'Install', ('Somepass1' | ConvertTo-SecureString -AsPlainText -Force)
        }

        $null = $scriptContent.AppendLine("Set-LabInstallationCredential -UserName $($Credential.UserName) -Password $($Credential.GetNetworkCredential().Password)")
        
        if ($Recipe.DeployRole -contains 'Domain' -or $Recipe.DeployRole -contains 'Exchange')
        {
            $null = $scriptContent.AppendLine("Add-LabDomainDefinition -Name $($Recipe.DefaultDomainName) -AdminUser $($Credential.UserName) -AdminPassword $($Credential.GetNetworkCredential().Password)")
            $null = $scriptContent.AppendLine("`$PSDefaultParameterValues.Add('Add-LabMachineDefinition:DomainName', '$($Recipe.DefaultDomainName)')")
            $null = $scriptContent.AppendLine("Add-LabMachineDefinition -Name $($Recipe.VmPrefix)DC1 -Roles RootDC")
        }

        if ($Recipe.DeployRole -contains 'PKI')
        {
            $null = $scriptContent.AppendLine("Add-LabMachineDefinition -Name $($Recipe.VmPrefix)CA1 -Roles CARoot")
        }

        if ($Recipe.DeployRole -contains 'Exchange')
        {
            $null = $scriptContent.AppendLine('$role = Get-LabPostInstallationActivity -CustomRole Exchange2016')
            $null = $scriptContent.AppendLine("Add-LabMachineDefinition -Name $($Recipe.VmPrefix)EX1 -PostInstallationActivity `$role")
        }

        if ($Recipe.DeployRole -contains 'SQL' -or $Recipe.DeployRole -contains 'CI/CD')
        {
            $null = $scriptContent.AppendLine("Add-LabMachineDefinition -Name $($Recipe.VmPrefix)SQL1 -Roles SQLServer2017")
        }

        if ($Recipe.DeployRole -contains 'CI/CD')
        {
            $null = $scriptContent.AppendLine("Add-LabMachineDefinition -Name $($Recipe.VmPrefix)CICD1 -Roles Tfs2017")
        }

        if ($Recipe.DeployRole -contains 'DSCPull')
        {
            $engine = if ($Recipe.DefaultOperatingSystem -like '*2019*') {'sql'} else {'mdb'}
            $null = $scriptContent.AppendLine("`$role = Get-LabMachineRoleDefinition -Role DSCPullServer -Properties @{DoNotPushLocalModules = 'true'; DatabaseEngine = '$engine'; SqlServer = '$($Recipe.VmPrefix)SQL1'; DatabaseName = 'DSC' }")
            $null = $scriptContent.AppendLine("Add-LabMachineDefinition -Name $($Recipe.VmPrefix)PULL01 -Roles `$role")
        }

        $null = $scriptContent.AppendLine('Install-Lab')
        $null = $scriptContent.AppendLine('Show-LabDeploymentSummary -Detailed')
        $labBlock = [scriptblock]::Create($scriptContent.ToString())

        if ($OutFile)
        {
            $scriptContent.ToString() | Set-Content -Path $OutFile -Force -Encoding UTF8
        }

        if ($PassThru) {$labBlock}
        if ($NoDeploy) { return }        

        if ($PSCmdlet.ShouldProcess($Recipe.Name, "Deploying lab"))
        {
            & $labBlock
        }
    }
}