Private/_MainFunctions.ps1
function Get-JwtTokenClaimsForPA { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$JwtToken ) $tokenSplit = $JwtToken.Split(".") $claimsSegment = $tokenSplit[1].Replace(" ", "+").Replace("-", "+"); $mod = $claimsSegment.Length % 4 if ($mod -gt 0) { $paddingCount = 4 - $mod; for ($i = 0; $i -lt $paddingCount; $i++) { $claimsSegment += "=" } } $decodedClaimsSegment = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($claimsSegment)) return ConvertFrom-Json $decodedClaimsSegment } Function Authenticate-PowerPlatform($user) { $authBaseUri = switch ($Endpoint) { "usgovhigh" { "https://login.microsoftonline.us" } "dod" { "https://login.microsoftonline.us" } default { "https://login.windows.net" } }; [string]$Endpoint = "prod" [string]$Audience = "https://management.azure.com/" [string]$TenantID = $null [string]$CertificateThumbprint = $null [string]$ClientSecret = $null #[string]$ApplicationId = "1950a258-227b-4e31-a9cf-717495945fc2" $azTokenCache = Get-Content $env:USERPROFILE\.azure\accessTokens.json | ConvertFrom-Json $authResult = $azTokenCache | Where-Object resource -EQ $Audience | Where-Object userId -eq $user if (!$authResult) { $getToken = az account get-access-token --resource https://management.azure.com/ -s $global:devops_DataVerseADSubscription $azTokenCache = Get-Content $env:USERPROFILE\.azure\accessTokens.json | ConvertFrom-Json $authResult = $azTokenCache | Where-Object resource -EQ $Audience | Where-Object userId -eq $user } [string]$at = $authResult.AccessToken $claims = Get-JwtTokenClaimsForPA -JwtToken $authResult.AccessToken $global:currentSession = @{ loggedIn = $true; idToken = $authResult.IdToken; upn = $claims.upn; tenantId = $claims.tid; userId = $claims.oid; applicationId = $claims.appid; certificateThumbprint = $CertificateThumbprint; clientSecret = $ClientSecret; secureClientSecret = $SecureClientSecret; refreshToken = $authResult.RefreshToken; expiresOn = (Get-Date).AddHours(8); resourceTokens = @{ $Audience = @{ accessToken = $authResult.AccessToken; expiresOn = $authResult.ExpiresOn; } }; selectedEnvironment = "~default"; authBaseUri = $authBaseUri; flowEndpoint = switch ($Endpoint) { "prod" { "api.flow.microsoft.com" } "usgov" { "gov.api.flow.microsoft.us" } "usgovhigh" { "high.api.flow.microsoft.us" } "dod" { "api.flow.appsplatform.us" } "china" { "api.powerautomate.cn" } "preview" { "preview.api.flow.microsoft.com" } "tip1" { "tip1.api.flow.microsoft.com" } "tip2" { "tip2.api.flow.microsoft.com" } default { throw "Unsupported endpoint '$Endpoint'" } }; powerAppsEndpoint = switch ($Endpoint) { "prod" { "api.powerapps.com" } "usgov" { "gov.api.powerapps.us" } "usgovhigh" { "high.api.powerapps.us" } "dod" { "api.apps.appsplatform.us" } "china" { "api.powerapps.cn" } "preview" { "preview.api.powerapps.com" } "tip1" { "tip1.api.powerapps.com" } "tip2" { "tip2.api.powerapps.com" } default { throw "Unsupported endpoint '$Endpoint'" } }; bapEndpoint = switch ($Endpoint) { "prod" { "api.bap.microsoft.com" } "usgov" { "gov.api.bap.microsoft.us" } "usgovhigh" { "high.api.bap.microsoft.us" } "dod" { "api.bap.appsplatform.us" } "china" { "api.bap.partner.microsoftonline.cn" } "preview" { "preview.api.bap.microsoft.com" } "tip1" { "tip1.api.bap.microsoft.com" } "tip2" { "tip2.api.bap.microsoft.com" } default { throw "Unsupported endpoint '$Endpoint'" } }; graphEndpoint = switch ($Endpoint) { "prod" { "graph.windows.net" } "usgov" { "graph.windows.net" } "usgovhigh" { "graph.windows.net" } "dod" { "graph.windows.net" } "china" { "graph.windows.net" } "preview" { "graph.windows.net" } "tip1" { "graph.windows.net" } "tip2" { "graph.windows.net" } default { throw "Unsupported endpoint '$Endpoint'" } }; cdsOneEndpoint = switch ($Endpoint) { "prod" { "api.cds.microsoft.com" } "usgov" { "gov.api.cds.microsoft.us" } "usgovhigh" { "high.api.cds.microsoft.us" } "dod" { "dod.gov.api.cds.microsoft.us" } "preview" { "preview.api.cds.microsoft.com" } "tip1" { "tip1.api.cds.microsoft.com" } "tip2" { "tip2.api.cds.microsoft.com" } default { throw "Unsupported endpoint '$Endpoint'" } }; }; } function Add-ClientSecrettoKeyVault() { $keyName = "$($global:devops_gitRepo)-clientsecret" Write-Host "Adding Client Secret to KeyVault with name $keyName" $global:devops_projectFile.ClientSecretAKVName = $keyName $kvsuccess = az keyvault secret set --name $keyName --vault-name $global:devops_AzureKeyVault --value $global:clientSecret --subscription $global:devops_selectedSubscription Write-Host "Removing Client Secret from Local Store" $global:devops_configFile.Projects[$global:devops_projectConfigID].ClientSecret = $null $global:devops_configFile | ConvertTo-Json | Set-Content ("$env:APPDATA\Microsoft.PowerPlatform.DevOps\devopsConfig.json") pause } Function Configure-AzureKeyVault() { [array]$options = "[Go Back]", "Connect to Existing KeyVault", "Create New KeyVault" do { $sel = Invoke-Menu -MenuTitle "---- Configure Azure KeyVault ------" -MenuOptions $options $akvSelection = $sel } until ($akvSelection -ge 0) switch ($akvSelection) { '0' { Show-ConfigMenu } '1' { Write-Host "Connecting to Existing KeyVault" if (!$global:devops_selectedSubscription) { Display-AzureAccounts } [array]$kvList = az keyvault list --subscription $global:devops_selectedSubscription --query [].name --output json | ConvertFrom-Json | Sort-Object if (!$kvList) { Write-Host "No KeyVaults exist for the Current Subscription, please create one" pause } else { do { $sel = Invoke-Menu -MenuTitle "---- Select Azure KeyVault ------" -MenuOptions $kvlist $akvToUse = $sel } until ($akvToUse -ge 0) $global:devops_AzureKeyVault = $kvList[$sel] $global:clientSecret = ($global:devops_configFile.Projects[$global:devops_projectConfigID].ClientSecret) | ConvertTo-SecureString if ($global:clientSecret) { Add-ClientSecrettoKeyVault } $global:devops_projectFile.selectedSubscription = $global:devops_selectedSubscription $global:devops_projectFile.selectedSubscriptionName = $global:devops_selectedSubscriptionName $global:devops_projectFile.AzureKeyVaultName = $global:devops_AzureKeyVault $global:devops_projectFile | ConvertTo-Json | Set-Content ("$global:devops_projectLocation\$global:devops_gitRepo.json") } Show-ConfigMenu } '2' { Write-Host "Create New KeyVault" if (!$global:devops_selectedSubscription) { Display-AzureAccounts } Write-Host "" $subconfirm = Read-Host "A New Azure KeyVault will be created in the $global:devops_selectedSubscriptionName [Press Enter to Continue or [Q] to quit]" if ($subconfirm -eq 'Q') { Show-ConfigMenu } try { az account set --subscription $global:devops_selectedSubscription [array]$locationlist = az account list-locations --query [].name --output table | Sort-Object do { $sel = Invoke-Menu -MenuTitle "---- Select location for the KeyVault ------" -MenuOptions $locationlist $locToUse = $sel } until ($locToUse -ge 0) $location = $locationlist[$sel] $resourcegroup = Read-Host "Enter the Resource Group to add the KeyVault to" $kvName = Read-Host "Enter the name for the KeyVault" $rgExists = az group list --query "[?name=='$resourcegroup']" | ConvertFrom-Json if (!$rgExists) { Write-Host "Resource group does not Exist... Creating" $rg = az group create --location $location --name $resourcegroup } $createdKV = az keyvault create --location $location --resource-group $resourcegroup --name $kvName $global:devops_AzureKeyVault = $kvName $global:clientSecret = ($global:devops_configFile.Projects[$global:devops_projectConfigID].ClientSecret) | ConvertTo-SecureString if ($global:clientSecret) { Add-ClientSecrettoKeyVault } $global:devops_projectFile.selectedSubscription = $global:devops_selectedSubscription $global:devops_projectFile.selectedSubscriptionName = $global:devops_selectedSubscriptionName $global:devops_projectFile.AzureKeyVaultName = $global:devops_AzureKeyVault $global:devops_projectFile | ConvertTo-Json | Set-Content ("$global:devops_projectLocation\$global:devops_gitRepo.json") } catch { Write-Host $_ pause } Show-ConfigMenu } Default {} } } function Configure-ServicePrincipal() { [array]$options = "[Go Back]", "Select Existing Service Principal", "Create New Service Principal" do { $sel = Invoke-Menu -MenuTitle "---- Configure Service Principal ------" -MenuOptions $options $spSelection = $sel } until ($spSelection -ge 0) switch ($spSelection) { '0' { Show-ConfigMenu } '1' { Write-Host "Select Existing Service Principal" if (!$global:devops_selectedSubscription) { Display-AzureAccounts } Write-Host "Retrieving available Service Principals.... please wait" [array]$spList = az ad sp list --all --query "[?signInAudience=='AzureADMyOrg'].{Name:displayName, ID:appId, Tenant:appOwnerTenantId} " --output json | ConvertFrom-Json | Sort-Object if (!$spList) { Write-Host "No Service Principals exist for the Current Subscription, please create one" pause } else { [array]$spmenu = $spList | ForEach-Object { $_.Name } do { $sel = Invoke-Menu -MenuTitle "---- Select Service Principal ------" -MenuOptions $spmenu $spToUse = $sel } until ($spToUse -ge 0) $global:devops_ClientID = $spList[$sel].ID $global:devops_TenantID = $spList[$sel].Tenant $global:devops_projectFile.ClientID = $global:devops_ClientID $global:devops_projectFile.TenantID = $global:devops_TenantID $CSecret = Read-Host "Enter the Client Secret for $global:devops_ClientID (or leave blank to create a new one)" if (!$CSecret) { Write-Host "No Secret provided .... creating new one" $newCreds = az ad sp credential reset --name $global:devops_ClientID --append --credential-description "Devops" | ConvertFrom-Json $SS = ConvertTo-SecureString -AsPlainText -String $newCreds.password -Force | ConvertFrom-SecureString $updatedProject = [ordered]@{ID = $global:devops_configFile.Projects[$global:devops_projectConfigID].ID; Name = $global:devops_configFile.Projects[$global:devops_projectConfigID].Name; ProjectLocation = $global:devops_projectLocation; ClientSecret = $SS; Tenant = $newCred.tenant } $global:devops_configFile.Projects[$global:devops_projectConfigID] = $updatedProject if ($global:devops_AzureKeyVault) { $global:clientSecret = $newCreds.password Add-ClientSecrettoKeyVault } $global:devops_configFile | ConvertTo-Json | Set-Content ("$env:APPDATA\Microsoft.PowerPlatform.DevOps\devopsConfig.json") } else { $SS = ConvertTo-SecureString -AsPlainText -String $CSecret -Force | ConvertFrom-SecureString $updatedProject = [ordered]@{ID = $global:devops_configFile.Projects[$global:devops_projectConfigID].ID; Name = $global:devops_configFile.Projects[$global:devops_projectConfigID].Name; ProjectLocation = $global:devops_projectLocation; ClientSecret = $SS } $global:devops_configFile.Projects[$global:devops_projectConfigID] = $updatedProject if ($global:devops_AzureKeyVault) { $global:clientSecret = $CSecret Add-ClientSecrettoKeyVault } $global:devops_configFile | ConvertTo-Json | Set-Content ("$env:APPDATA\Microsoft.PowerPlatform.DevOps\devopsConfig.json") } $global:devops_projectFile | ConvertTo-Json | Set-Content ("$global:devops_projectLocation\$global:devops_gitRepo.json") } Show-ConfigMenu } '2' { Write-Host "Create New Service Principal" if (!$global:devops_selectedSubscription) { Display-AzureAccounts } Write-Host "" $subconfirm = Read-Host "A New Azure KeyVault will be created in the $global:devops_selectedSubscriptionName [Press Enter to Continue or [Q] to quit]" if ($subconfirm -eq 'Q') { Show-ConfigMenu } try { az account set --subscription $global:devops_selectedSubscription [array]$locationlist = az account list-locations --query [].name --output table | Sort-Object do { $sel = Invoke-Menu -MenuTitle "---- Select location for the KeyVault ------" -MenuOptions $locationlist $locToUse = $sel } until ($locToUse -ge 0) $location = $locationlist[$sel] $resourcegroup = Read-Host "Enter the Resource Group to add the KeyVault to" $kvName = Read-Host "Enter the name for the KeyVault" $rgExists = az group list --query "[?name=='$resourcegroup']" | ConvertFrom-Json if (!$rgExists) { Write-Host "Resource group does not Exist... Creating" $rg = az group create --location $location --name $resourcegroup } $createdKV = az keyvault create --location $location --resource-group $resourcegroup --name $kvName $global:devops_AzureKeyVault = $kvName $global:devops_projectFile.selectedSubscription = $global:devops_selectedSubscription $global:devops_projectFile.selectedSubscriptionName = $global:devops_selectedSubscriptionName $global:devops_projectFile.AzureKeyVaultName = $global:devops_AzureKeyVault $global:devops_projectFile | ConvertTo-Json | Set-Content ("$global:devops_projectLocation\$global:devops_gitRepo.json") } catch { Write-Host $_ pause } pause Show-ConfigMenu } Default {} } } function Set-ColourOption( [string]$value ) { if ($value -eq "False") { return "White" } elseif ($value -eq "Error") { return "Red" } elseif ($value -eq "Optional") { return "Magenta" } else { return "Green" } } Function Display-AzureAccounts() { $AZAccounts = az account list | ConvertFrom-Json [array]$options = "Login to a New Account" $options += $AZAccounts | ForEach-Object { "$($_.name) $($_.user.name)" } do { $sel = Invoke-Menu -MenuTitle "---- Please Select your Subscription ------" -MenuOptions $options } until ($sel -ge 0) if ($sel -eq 0) { az login --use-device-code --allow-no-subscriptions #Get Azure Accounts / Subscriptions $AZAccounts = az account list | ConvertFrom-Json Display-AzureAccounts } else { $global:devops_selectedSubscription = $AZAccounts[$sel - 1].id $global:devops_selectedSubscriptionName = $AZAccounts[$sel - 1].name } } function Update-ProjectFile() { $projectFileMaster = Get-Content (Join-Path (Get-Module -Name microsoft.powerplatform.devops).Path -ChildPath ..\Private\emptyProject.json) | ConvertFrom-Json if ($projectFileMaster) { $properties = Get-Member -InputObject $global:devops_projectFile -MemberType Properties $properties | ForEach-Object { $projectFileMaster.$($_.Name) = $global:devops_projectFile.$($_.Name) } $global:devops_projectFile = $projectFileMaster $global:devops_projectFile | ConvertTo-Json | Set-Content ("$global:devops_projectLocation\$global:devops_gitRepo.json") } } Function Display-AzureLogins() { [array]$options = "Login to a New Account" $AZCreds = az account list --query '[].{Name:user.name, Type:user.type, ID:id, Subscription:name}' --output json | ConvertFrom-Json $UniqueCreds = $AZCreds | Sort-Object -Property * -Unique $options += $UniqueCreds | ForEach-Object { "$($_.Name) ($($_.Type))($($_.Subscription))" } do { $sel = Invoke-Menu -MenuTitle "---- Please Select your AAD Subscription ------" -MenuOptions $options } until ($sel -ge 0) if ($sel -eq 0) { az login --use-device-code --allow-no-subscriptions Display-AzureLogins } else { $global:devops_DataverseEmail = $UniqueCreds[$sel - 1].Name $global:devops_DataVerseADSubscription = $UniqueCreds[$sel - 1].id $global:devops_DataverseCredType = $UniqueCreds[$sel - 1].Type } } function Format-Json { <# .SYNOPSIS Prettifies JSON output. .DESCRIPTION Reformats a JSON string so the output looks better than what ConvertTo-Json outputs. .PARAMETER Json Required: [string] The JSON text to prettify. .PARAMETER Minify Optional: Returns the json string compressed. .PARAMETER Indentation Optional: The number of spaces (1..1024) to use for indentation. Defaults to 4. .PARAMETER AsArray Optional: If set, the output will be in the form of a string array, otherwise a single string is output. .EXAMPLE $json | ConvertTo-Json | Format-Json -Indentation 2 #> [CmdletBinding(DefaultParameterSetName = 'Prettify')] Param( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [string]$Json, [Parameter(ParameterSetName = 'Minify')] [switch]$Minify, [Parameter(ParameterSetName = 'Prettify')] [ValidateRange(1, 1024)] [int]$Indentation = 4, [Parameter(ParameterSetName = 'Prettify')] [switch]$AsArray ) if ($PSCmdlet.ParameterSetName -eq 'Minify') { return ($Json | ConvertFrom-Json) | ConvertTo-Json -Depth 100 -Compress } # If the input JSON text has been created with ConvertTo-Json -Compress # then we first need to reconvert it without compression if ($Json -notmatch '\r?\n') { $Json = ($Json | ConvertFrom-Json) | ConvertTo-Json -Depth 100 } $indent = 0 $regexUnlessQuoted = '(?=([^"]*"[^"]*")*[^"]*$)' $result = $Json -split '\r?\n' | ForEach-Object { # If the line contains a ] or } character, # we need to decrement the indentation level unless it is inside quotes. if ($_ -match "[}\]]$regexUnlessQuoted") { $indent = [Math]::Max($indent - $Indentation, 0) } # Replace all colon-space combinations by ": " unless it is inside quotes. $line = (' ' * $indent) + ($_.TrimStart() -replace ":\s+$regexUnlessQuoted", ': ') # If the line contains a [ or { character, # we need to increment the indentation level unless it is inside quotes. if ($_ -match "[\{\[]$regexUnlessQuoted") { $indent += $Indentation } $line } if ($AsArray) { return $result } return $result -Join [Environment]::NewLine } Function Invoke-Menu() { Param( [Parameter(Mandatory = $True)][String]$MenuTitle, [Parameter(Mandatory = $True)][array]$MenuOptions ) $MaxValue = $MenuOptions.count - 1 $Selection = 0 $EnterPressed = $False Clear-Host While ($EnterPressed -eq $False) { Write-Host "$MenuTitle" For ($i = 0; $i -le $MaxValue; $i++) { If ($i -eq $Selection) { Write-Host -BackgroundColor Cyan -ForegroundColor Black "[ $($MenuOptions[$i]) ]" } Else { Write-Host " $($MenuOptions[$i]) " } } $KeyInput = $host.ui.rawui.readkey("NoEcho,IncludeKeyDown").virtualkeycode Switch ($KeyInput) { 13 { $EnterPressed = $True Return $Selection Clear-Host break } 38 { If ($Selection -eq 0) { $Selection = $MaxValue } Else { $Selection -= 1 } Clear-Host break } 40 { If ($Selection -eq $MaxValue) { $Selection = 0 } Else { $Selection += 1 } Clear-Host break } Default { Clear-Host } } } } function Copy-GitRepoFromTemplate { try { if ($global:devops_Core) { $path = Read-Host "Enter a folder to create a new git Repository for your Power Platform Project" } else { Write-Host "Select a folder to create a new git Repository for your Power Platform Project" $application = New-Object -ComObject Shell.Application $path = ($application.BrowseForFolder(0, 'Select a folder', 0)).Self.Path } $global:devops_projectLocation = "$path\$global:devops_gitRepo" if (!(Test-Path -Path "$global:devops_projectLocation") -and !($null -eq $path)) { Write-Host "Creating Project Folder" New-Item -Path $path -Name $global:devops_gitRepo -ItemType Directory Copy-Item -Path (Join-Path $PSScriptRoot emptyProject.json) -Destination $global:devops_projectLocation\$global:devops_gitRepo.json $newProject = @([ordered]@{ID = $global:devops_configFile.Projects.Count; Name = $global:devops_gitRepo; ProjectLocation = "$global:devops_projectLocation" }) $global:devops_configFile.Projects += $newProject $global:devops_configFile | ConvertTo-Json | Set-Content ("$env:APPDATA\Microsoft.PowerPlatform.DevOps\devopsConfig.json") $global:devops_projectFile = Get-Content ("$global:devops_projectLocation\$global:devops_gitRepo.json") | ConvertFrom-Json Update-ProjectFile Set-Location -Path "$global:devops_projectLocation" $message = "Setting up PowerPlatform.DevOps locally" Write-Host $message Remove-Item -Path (Join-Path $PSScriptRoot ..\FrameworkTemplate\node_modules) -Recurse -Force -ErrorAction Ignore Copy-Item -Path (Join-Path $PSScriptRoot ..\FrameworkTemplate\*) -Destination $global:devops_projectLocation\ -Recurse -Force $message = "Confirming Git User Details" Write-Host $message $GitUser = git config --global user.name $GitEmail = git config --global user.email If ($null -eq $GitUser) { $GitUser = Read-Host "Enter your name (to use when committing changes to Git)" git config --global user.name $GitUser } If ($null -eq $GitEmail) { $GitEmail = Read-Host "Enter your email address (to use when committing changes to Git)" git config --global user.email $GitEmail } Remove-Item .git -Recurse -Force -ErrorAction SilentlyContinue git init Write-Host "Updating Build.yaml ..." (Get-Content -Path $global:devops_projectLocation\build.yaml) -replace "replaceRepo", $global:devops_gitRepo | Set-Content -Path $global:devops_projectLocation\build.yaml Write-Host "Rename PowerPlatformDevOps.sln to $global:devops_gitRepo.sln" Rename-Item -Path $global:devops_projectLocation\PowerPlatformDevOps.sln -NewName "$global:devops_gitRepo.sln" Write-Host "Rename dot files" Rename-Item -Path $global:devops_projectLocation\file.gitignore -NewName ".gitignore" Rename-Item -Path $global:devops_projectLocation\file.artifactignore -NewName ".artifactignore" Rename-Item -Path $global:devops_projectLocation\file.gitattributes -NewName ".gitattributes" $global:devops_projectFile.gitRepo = $global:devops_gitRepo $global:devops_projectFile | ConvertTo-Json | Set-Content ("$global:devops_projectLocation\$global:devops_gitRepo.json") } else { Write-Warning "The Path $global:devops_projectLocation already exists, please select a different path or project name" $continue = Read-Host -Prompt "Press [Enter] to Continue or [Q] to Quit" if (!$continue -eq 'Q') { } } } catch { } } function Add-GitRepo { do { $global:devops_gitRepo = Read-Host -Prompt "Please enter a Name for the Git Repository you wish to Create" }until ($global:devops_gitRepo -ne "") if (!(Test-Path $env:APPDATA\Microsoft.PowerPlatform.DevOps)) { New-Item -Path $env:APPDATA\Microsoft.PowerPlatform.Devops -ItemType Directory } if (!($global:devops_configFile.PSobject.Properties.Project -contains "Name") -or !($global:devops_configFile.Projects.Name.Contains($global:devops_gitRepo))) { Copy-GitRepoFromTemplate pause } else { Write-Host "You already have a repo called $global:devops_gitRepo, please select a different name" $global:devops_gitRepo = "" Add-GitRepo } } function Select-GitRepo { if ($global:devops_configFile.Projects.Count -gt 0) { [array]$options = "Go Back" $options += $global:devops_configFile.Projects | ForEach-Object { "$($_.Name)" } do { $thesel = Invoke-Menu -MenuTitle "---- Please Select your Project ------" -MenuOptions $options $sel = $thesel - 1 $selectedRepo = $global:devops_configFile.Projects[$sel].Name $global:devops_projectConfigID = $sel } until ($selectedRepo -ne "") if ($thesel -eq 0) { return } if (($global:devops_configFile.Projects[$sel].PSobject.Properties.Name -contains "ProjectLocation")) { $global:devops_projectFile = Get-Content ("$($global:devops_configFile.Projects[$sel].ProjectLocation)\$selectedRepo.json") | ConvertFrom-Json Update-ProjectFile $global:devops_projectLocation = $global:devops_configFile.Projects[$sel].ProjectLocation } else { $global:devops_projectFile = Get-Content ("$env:APPDATA\Microsoft.PowerPlatform.Devops\$selectedRepo\$selectedRepo.json") | ConvertFrom-Json $global:devops_projectLocation = $global:devops_projectFile.ProjectLocation if (! (Test-Path -Path "$global:devops_projectLocation\$selectedRepo.json")) { Copy-Item -Path "$env:APPDATA\Microsoft.PowerPlatform.Devops\$selectedRepo\$selectedRepo.json" -Destination " $global:devops_projectLocation\" } $updatedProject = [ordered]@{ID = $global:devops_configFile.Projects[$sel].ID; Name = $global:devops_configFile.Projects[$sel].Name; ProjectLocation = $global:devops_projectLocation } $global:devops_configFile.Projects[$sel] = $updatedProject $global:devops_configFile | ConvertTo-Json | Set-Content ("$env:APPDATA\Microsoft.PowerPlatform.DevOps\devopsConfig.json") $global:devops_projectFile = Get-Content ("$global:devops_projectLocation\$selectedRepo.json") | ConvertFrom-Json Update-ProjectFile } Set-Location $global:devops_projectLocation $global:devops_projectTitle = $global:devops_projectFile.gitRepo $global:devops_gitRepo = $global:devops_projectFile.gitRepo $global:devops_selectedSubscriptionName = $global:devops_projectFile.selectedSubscriptionName $global:devops_selectedSubscription = $global:devops_projectFile.selectedSubscription $global:devops_AzureKeyVault = $global:devops_projectFile.AzureKeyVaultName $global:devops_ClientID = $global:devops_projectFile.ClientID $global:devops_TenantID = $global:devops_projectFile.TenantID } else { Write-Host "You don't have any Projects set up, please Add a new one or Clone an existing Repo" pause } } function Clone-GitRepo { $regEx = '(http[s]?|[s]?ftp[s]?)(:\/\/)([^\s,]+)' do { $repoURL = Read-Host "Please enter the git clone URL" } until ($repoURL -ne "" -and $repoURL -match $regEx) if ($global:devops_Core) { $path = Read-Host "Please enter the local path to Clone to" } else { $application = New-Object -ComObject Shell.Application $path = ($application.BrowseForFolder(0, "Select a folder to Clone your $selectedRepo Repo to", 0)).Self.Path } $repoFinder = $repoURL -split "/" $selectedRepo = $repoFinder[$repoFinder.Count - 1].Replace('.git', '') $global:devops_gitRepo = $selectedRepo Write-Host $selectedRepo Write-host $path Write-Host $repoURL $isADO = $repoURL.Contains("azure.com") -or $repoURL.Contains("visualstudio.com") if ($isADO) { $AZAccounts = az account list | ConvertFrom-Json if (!($global:devops_selectedSubscription -ne "" -and (($AZAccounts | ForEach-Object { $_.id }) -contains $global:devops_selectedSubscription))) { if ($AZAccounts.Length -eq 0) { Write-Host "No Accounts Found, please login" az login --use-device-code --allow-no-subscriptions $AZAccounts = az account list | ConvertFrom-Json Display-AzureAccounts } else { Display-AzureAccounts } } az account set --subscription $global:devops_selectedSubscription #Get Access Token $azureDevopsResourceId = "499b84ac-1321-427f-aa17-267ca6975798" $token = az account get-access-token --resource $azureDevopsResourceId | ConvertFrom-Json $authValue = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(":" + $token.accessToken)) . git -c http.$repoURL.extraHeader="Authorization: Basic $authValue" clone $repoURL $path\$global:devops_gitRepo } else { . git clone $repoURL $path\$global:devops_gitRepo } $global:devops_projectLocation = "$path\$selectedRepo" if (!(Test-Path -Path "$global:devops_projectLocation\$selectedRepo.json")) { Copy-Item -Path (Join-Path $PSScriptRoot emptyProject.json) -Destination $global:devops_projectLocation\$selectedRepo.json $global:devops_projectFile = Get-Content ("$global:devops_projectLocation\$selectedRepo.json") | ConvertFrom-Json Update-ProjectFile $global:devops_projectFile.gitRepo = $global:devops_gitRepo $global:devops_projectFile | ConvertTo-Json | Set-Content ("$global:devops_projectLocation\$global:devops_gitRepo.json") } $newProject = [ordered]@{ID = $global:devops_configFile.Projects.Count; Name = $selectedRepo; ProjectLocation = $global:devops_projectLocation } $global:devops_configFile.Projects += $newProject $global:devops_configFile | ConvertTo-Json | Set-Content ("$env:APPDATA\Microsoft.PowerPlatform.DevOps\devopsConfig.json") $global:devops_projectFile = Get-Content ("$global:devops_projectLocation\$global:devops_gitRepo.json") | ConvertFrom-Json Update-ProjectFile Set-Location $global:devops_projectLocation $global:devops_projectTitle = $global:devops_projectFile.gitRepo git config http.$repoURL.extraHeader "Authorization:Basic $authValue" pause } function Get-GitRepo { if (!(Test-Path $env:APPDATA\Microsoft.PowerPlatform.DevOps)) { New-Item -Path $env:APPDATA\Microsoft.PowerPlatform.Devops -ItemType Directory } if ($global:devops_Core) { $path = Read-Host "Please enter the location of your Existing Project" } else { $application = New-Object -ComObject Shell.Application $path = ($application.BrowseForFolder(0, "Select the location of your Existing Project", 0)).Self.Path } $repoFinder = $path -split "\\" $selectedRepo = $repoFinder[$repoFinder.Count - 1] Write-Host $selectedRepo Write-Host $path\$selectedRepo.json if ((Test-Path -Path "$path\$selectedRepo.json")) { $global:devops_projectLocation = "$path" $global:devops_projectFile = Get-Content ("$path\$selectedRepo.json") | ConvertFrom-Json Update-ProjectFile $newProject = [ordered]@{ID = $global:devops_configFile.Projects.Count; Name = $selectedRepo; ProjectLocation = $global:devops_projectLocation } $global:devops_configFile.Projects += $newProject $global:devops_configFile | ConvertTo-Json | Set-Content ("$env:APPDATA\Microsoft.PowerPlatform.DevOps\devopsConfig.json") $global:devops_gitRepo = $global:devops_projectFile.gitRepo Set-Location $global:devops_projectLocation } else { Write-Host "$path is not a valid Project Location, please Add a new one or Clone an existing Repo" pause } } |