Private/Add-CICD.ps1

#
# _Add-CICD.ps1
#

function Add-CICDEnvironment {
    if (!$VerbosePreference -eq "Continue") {
    Clear-Host    
}
    $message = "Configuring CI/CD Environment"
    Write-Host $message

    try {
        $CreateOrSelectEnv = Read-Host -Prompt "CI/CD Environment : Would you like to [P]rovision a new Power Platform Environment ? or [S]elect an Existing One (Default [S])"
        if ($CreateOrSelectEnv -eq "P") {
            Add-Environment
            Invoke-ConfigureCICD
        }
        else {
            Invoke-ConfigureCICD
        }
    }
    catch {
        $global:devops_projectFile.CICDEnvironmentName = "Error"
        pause
    }
    $global:devops_projectFile | ConvertTo-Json | Set-Content ("$global:devops_projectLocation\$global:devops_gitRepo.json") 
   
}

function Invoke-ConfigureCICD
{
    $scRepo = $global:devops_gitRepo.Replace(' ', '')
    $scOrg = $global:devops_projectFile.OrgName
    $scProject = $global:devops_projectFile.Project
    
    $message = "Connecting to Power Platform"
    Write-Host $message

    #Install-PowerAppsAdmin

    Get-DataverseLogin

    $connList = Get-AdminPowerAppEnvironment
    $options = $connList | ForEach-Object { "$($_.DisplayName) ($($_.Internal.properties.linkedEnvironmentMetadata.instanceUrl))" }

    do {
        $sel = Invoke-Menu -MenuTitle "---- Please Select your Continuous Deployment Environment ------" -MenuOptions $options
        $CICDEnvironment = $connList[$sel]
        $connCICD = Get-DataverseConnection($CICDEnvironment.Internal.properties.linkedEnvironmentMetadata.instanceUrl)
    } until ($connCICD -ne "")

    Write-Host $CICDEnvironment.Internal.properties.linkedEnvironmentMetadata.instanceUrl
    $friendlyEnvironmentName = $CICDEnvironment.Internal.properties.linkedEnvironmentMetadata.friendlyName -replace '[^a-zA-Z0-9]', ''

    Write-Host "Adding Environment to Environments.json"
    ConvertTo-Json -Depth 3 @([ordered]@{EnvironmentName = $friendlyEnvironmentName; EnvironmentURL = $CICDEnvironment.Internal.properties.linkedEnvironmentMetadata.instanceUrl; PreAction = "false"; PreFunctions = @(); PostAction = "false"; PostFunctions = @()}) | Format-Json | Out-File .\Environments.json
   
    if ($global:devops_DataverseCredType -eq "user") {
        if (!($global:Password) -or !($global:devops_DataverseEmail)) {
            Do {
                $Credentials = Get-Credential -Message "Enter the Credentials the Pipeline should use for Deploying to D365 / CDS"
            } Until (($Credentials.GetNetworkCredential().UserName -ne "") -and ($Credentials.GetNetworkCredential().Password -ne "")) 
            $username = $Credentials.GetNetworkCredential().UserName
            $password = $Credentials.GetNetworkCredential().Password 
        }
        else {
            $username = $global:devops_DataverseEmail
            $password = $global:Password
        }
    }
    else {
        $username = $global:devops_ClientID
        $password = $global:clientSecret
    }
    
    $message = "Creating variable groups in Azure DevOps"
    Write-Host $message
    
    try {
        $varGroupCICD = az pipelines variable-group create --organization https://dev.azure.com/$scOrg --project $scProject --name "$scRepo.PowerPlatformCICD"  --variables d365username=$username --authorize $true | ConvertFrom-Json
        az pipelines variable-group variable create --organization https://dev.azure.com/$scOrg --project $scProject --name d365password --value $password --secret $true --group-id $varGroupCICD.id
        az pipelines variable-group variable create --organization https://dev.azure.com/$scOrg --project $scProject --name d365url --value $CICDEnvironment.Internal.properties.linkedEnvironmentMetadata.instanceUrl --group-id $varGroupCICD.id
    
        $message = "Creating Build and Deploy Pipeline in Azure DevOps"
        Write-Host $message
    
        $global:devops_projectFile.CICDEnvironmentName = $CICDEnvironment.DisplayName
        $global:devops_projectFile.CICDEnvironmentURL = $CICDEnvironment.Internal.properties.linkedEnvironmentMetadata.instanceUrl
        $global:devops_projectFile.CICDFriendlyName = $friendlyEnvironmentName
        $global:devops_projectFile.CICDVarGroupID = $varGroupCICD.id
        $global:devops_projectFile | ConvertTo-Json | Set-Content ("$global:devops_projectLocation\$global:devops_gitRepo.json")    
    
        $buildYAML = Get-Content -Path "$global:devops_projectLocation\Build.yaml"
        $azureYAML = Get-Content -Path  (Join-Path $PSScriptRoot ..\Snippets\Environment_CICD.yaml)

        $azureYAML = $azureYAML.Replace('environmentName_safe', $friendlyEnvironmentName)
        
        $azureYAML = $azureYAML.Replace('environmentName', $global:devops_projectFile.CICDFriendlyName)        
        $azureYAML = $azureYAML.Replace('replaceRepo', $scRepo)
        $buildYAML + $azureYAML | Set-Content -Path  "$global:devops_projectLocation\Build.yaml"

        git add -A
        git commit -m "Added CICD Environment to Build.yaml"
        git push origin master

        $pipeline = az pipelines create --organization https://dev.azure.com/$scOrg --project $scProject --name "$scRepo.CI" --yml-path /build.yaml --repository $scRepo --repository-type tfsgit --branch master | ConvertFrom-Json
        $global:devops_projectFile.CIPipeLineId = $pipeline.definition.id
        $global:devops_projectFile | ConvertTo-Json | Set-Content ("$global:devops_projectLocation\$global:devops_gitRepo.json")  
        az pipelines show --organization https://dev.azure.com/$scOrg --project $scProject --id $pipeline.definition.id --open
    }
    catch {
        Write-Host $_
        $global:devops_projectFile.CICDEnvironmentName = "Error"
        $global:devops_projectFile | ConvertTo-Json | Set-Content ("$global:devops_projectLocation\$global:devops_gitRepo.json") 
        pause   
    }
    
    
    
}

function Invoke-ConfigureTargetEnvironment
{
    $scRepo = $global:devops_gitRepo.Replace(' ', '')
    $scOrg = $global:devops_projectFile.OrgName
    $scProject = $global:devops_projectFile.Project
    
    $message = "Connecting to Power Platform"
    Write-Host $message

    Get-DataverseLogin

    $connList = Get-AdminPowerAppEnvironment
    $options = $connList | ForEach-Object { "$($_.DisplayName) ($($_.Internal.properties.linkedEnvironmentMetadata.instanceUrl))" }

    do {
        $sel = Invoke-Menu -MenuTitle "---- Please Select your Deployment Environment ------" -MenuOptions $options
        $TargetEnvironment = $connList[$sel]
        $connTarget = Get-DataverseConnection($TargetEnvironment.Internal.properties.linkedEnvironmentMetadata.instanceUrl)
    } until ($connTarget -ne "")

    Write-Host $TargetEnvironment.Internal.properties.linkedEnvironmentMetadata.instanceUrl
    $friendlyEnvironmentName = $TargetEnvironment.Internal.properties.linkedEnvironmentMetadata.friendlyName -replace '[^a-zA-Z0-9]', ''

    Write-Host "Adding Environment to Environments.json"
    $environmentsJSON = Get-Content .\Environments.json | ConvertFrom-Json
    $environmentsJSON += [ordered]@{EnvironmentName = $TargetEnvironment.Internal.properties.linkedEnvironmentMetadata.friendlyName; EnvironmentURL = $TargetEnvironment.Internal.properties.linkedEnvironmentMetadata.instanceUrl; PreAction = "false"; PreFunctions = @(); PostAction = "false"; PostFunctions = @()  }
    ConvertTo-Json -Depth 3 $environmentsJSON | Format-Json | Out-File .\Environments.json
   
    if ($global:devops_DataverseCredType -eq "user") {
        if (!($global:Password) -or !($global:devops_DataverseEmail)) {
            Do {
                $Credentials = Get-Credential -Message "Enter the Credentials the Pipeline should use for Deploying to D365 / CDS"
            } Until (($Credentials.GetNetworkCredential().UserName -ne "") -and ($Credentials.GetNetworkCredential().Password -ne "")) 
            $username = $Credentials.GetNetworkCredential().UserName
            $password = $Credentials.GetNetworkCredential().Password 
        }
        else {
            $username = $global:devops_DataverseEmail
            $password = $global:Password
        }
    }
    else {
        $username = $global:devops_ClientID
        $password = $global:clientSecret
    }
    
    $message = "Creating variable groups in Azure DevOps"
    Write-Host $message
    
    try {
        $varGroupCICD = az pipelines variable-group create --organization https://dev.azure.com/$scOrg --project $scProject --name "$scRepo.$friendlyEnvironmentName"  --variables d365username=$username --authorize $true | ConvertFrom-Json
        az pipelines variable-group variable create --organization https://dev.azure.com/$scOrg --project $scProject --name d365password --value $password --secret $true --group-id $varGroupCICD.id
        az pipelines variable-group variable create --organization https://dev.azure.com/$scOrg --project $scProject --name d365url --value $TargetEnvironment.Internal.properties.linkedEnvironmentMetadata.instanceUrl --group-id $varGroupCICD.id
    
        @{"description"="$friendlyEnvironmentName";"name"="$friendlyEnvironmentName"} | ConvertTo-Json | Out-File .\body.json -Encoding ascii

        az devops invoke --area distributedtask --resource environments --organization https://dev.azure.com/$scOrg --route-parameters project="$scProject" --in-file .\body.json --http-method post

        Remove-Item -Path .\body.json -Force 

        $message = "Updating Build and Deploy Pipeline in Azure DevOps"
        Write-Host $message
    
        $buildYAML = Get-Content -Path "$global:devops_projectLocation\Build.yaml"
        $azureYAML = Get-Content -Path  (Join-Path $PSScriptRoot ..\Snippets\Environment_CICD.yaml)

        $azureYAML = $azureYAML.Replace('environmentName_safe', $friendlyEnvironmentName)
        
        $azureYAML = $azureYAML.Replace('environmentName', $TargetEnvironment.Internal.properties.linkedEnvironmentMetadata.friendlyName)        
        $azureYAML = $azureYAML.Replace('replaceRepo', $scRepo)
        $azureYAML = $azureYAML.Replace('PowerPlatformCICD', $friendlyEnvironmentName)
        $buildYAML + $azureYAML | Set-Content -Path  "$global:devops_projectLocation\Build.yaml"
    }
    catch {
        Write-Host $_
        $global:devops_projectFile.TargetEnvironmentName = "Error"
        $global:devops_projectFile | ConvertTo-Json | Set-Content ("$global:devops_projectLocation\$global:devops_gitRepo.json") 
        pause   
    }
}