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 | Out-FileUtf8NoBom ("$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

    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-FileUtf8NoBom .\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 {
        $groups = az pipelines variable-group list --organization https://dev.azure.com/$scOrg --project $scProject  | ConvertFrom-Json
        $varGroupCICD = $groups | where { $_.name -eq "$scRepo.PowerPlatformCICD" }         
        if (!$varGroupCICD) {
            $varGroupCICD = az pipelines variable-group create --organization https://dev.azure.com/$scOrg --project $scProject --name "$scRepo.PowerPlatformCICD"  --variables d365username=$username --authorize $true | ConvertFrom-Json
        }

        $vars = az pipelines variable-group variable list --organization https://dev.azure.com/$scOrg --project $scProject --group-id $varGroupCICD.id | ConvertFrom-Json
        if (!$vars.d365username) {            
            az pipelines variable-group variable create --organization https://dev.azure.com/$scOrg --project $scProject --name d365username --value $username --group-id $varGroupCICD.id
        } else {
            az pipelines variable-group variable update --organization https://dev.azure.com/$scOrg --project $scProject --name d365username --value $username --group-id $varGroupCICD.id
        }        

        if (!$vars.d365password) {
            az pipelines variable-group variable create --organization https://dev.azure.com/$scOrg --project $scProject --name d365password --value="$password" --secret $true --group-id $varGroupCICD.id
        }
        else {
            az pipelines variable-group variable update --organization https://dev.azure.com/$scOrg --project $scProject --name d365password --value="$password" --secret $true --group-id $varGroupCICD.id
        }

        if (!$vars.d365url) {
            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
        } else {
            az pipelines variable-group variable update --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 | Out-FileUtf8NoBom ("$global:devops_projectLocation\$global:devops_gitRepo.json")    
    
        $buildYAML = Get-Content -Path "$global:devops_projectLocation\Build.yaml"

        if(($buildYAML -Match "$scRepo.PowerPlatformCICD").Length -gt 0) {
            Write-Host "***----Previous CI/CD Pipeline Stage Entry Detected ----****"
            Write-Host "From the $global:devops_projectLocation\Build.yaml File, remove the entire 'stage' that contains the group '$scRepo.PowerPlatformCICD'"
            pause
        }

        $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 | Out-FileUtf8NoBom "$global:devops_projectLocation\Build.yaml"


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

        $pipelines = az pipelines list --organization https://dev.azure.com/$scOrg --project $scProject --name "$scRepo.CI" | ConvertFrom-Json
        $pipeline = $pipelines | where { $_.name -eq "$scRepo.CI" }
        if(!$pipeline) {
            $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
        } else {
            az pipelines update --organization https://dev.azure.com/$scOrg --project $scProject --branch master --id $pipeline.id
            $global:devops_projectFile.CIPipeLineId = $pipeline.id
        }

        $global:devops_projectFile | ConvertTo-Json | Out-FileUtf8NoBom ("$global:devops_projectLocation\$global:devops_gitRepo.json")  
        az pipelines show --organization https://dev.azure.com/$scOrg --project $scProject --id $global:devops_projectFile.CIPipeLineId --open
    }
    catch {
        Write-Host $_
        $global:devops_projectFile.CICDEnvironmentName = "Error"
        $global:devops_projectFile | ConvertTo-Json | Out-FileUtf8NoBom ("$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 = $friendlyEnvironmentName; EnvironmentURL = $TargetEnvironment.Internal.properties.linkedEnvironmentMetadata.instanceUrl; PreAction = "false"; PreFunctions = @(); PostAction = "false"; PostFunctions = @() }
    ConvertTo-Json -Depth 3 $environmentsJSON | Format-Json | Out-FileUtf8NoBom .\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 {
        $groups = az pipelines variable-group list --organization https://dev.azure.com/$scOrg --project $scProject  | ConvertFrom-Json
        $varGroupCICD = $groups | where { $_.name -eq "$scRepo.$friendlyEnvironmentName" }         
        if (!$varGroupCICD) {
            $varGroupCICD = az pipelines variable-group create --organization https://dev.azure.com/$scOrg --project $scProject --name "$scRepo.$friendlyEnvironmentName"  --variables d365username=$username --authorize $true | ConvertFrom-Json
        }

        $vars = az pipelines variable-group variable list --organization https://dev.azure.com/$scOrg --project $scProject --group-id $varGroupCICD.id | ConvertFrom-Json
        if (!$vars.d365username) {            
            az pipelines variable-group variable create --organization https://dev.azure.com/$scOrg --project $scProject --name d365username --value $username --group-id $varGroupCICD.id
        } else {
            az pipelines variable-group variable update --organization https://dev.azure.com/$scOrg --project $scProject --name d365username --value $username --group-id $varGroupCICD.id
        }        

        if (!$vars.d365password) {
            az pipelines variable-group variable create --organization https://dev.azure.com/$scOrg --project $scProject --name d365password --value="$password" --secret $true --group-id $varGroupCICD.id
        }
        else {
            az pipelines variable-group variable update --organization https://dev.azure.com/$scOrg --project $scProject --name d365password --value="$password" --secret $true --group-id $varGroupCICD.id
        }

        if (!$vars.d365url) {
            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
        } else {
            az pipelines variable-group variable update --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-FileUtf8NoBom .\body.json 

        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"

        if(($buildYAML -Match "$scRepo.$friendlyEnvironmentName").Length -gt 0) {
            Write-Host "***----Previous CI/CD Pipeline Stage Entry Detected ----****"            
            Write-Host "From the $global:devops_projectLocation\Build.yaml File, remove the entire 'stage' that contains the group '$scRepo.$friendlyEnvironmentName'"
            pause
        }        

        $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 | Out-FileUtf8NoBom "$global:devops_projectLocation\Build.yaml"
    }
    catch {
        Write-Host $_
        $global:devops_projectFile.TargetEnvironmentName = "Error"
        $global:devops_projectFile | ConvertTo-Json | Out-FileUtf8NoBom ("$global:devops_projectLocation\$global:devops_gitRepo.json") 
        pause   
    }
}