RDG.Deployment.Utils.psm1
function Update-RDGDeploymentUtils { $moduleName = "RDG.Deployment.Utils" $repository = "PSGallery" # Check if the module is installed $installedModule = Get-Module -Name $moduleName -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1 if ($installedModule) { # Get information about the latest version of the module available in the repository $latestModule = Find-Module -Name $moduleName -Repository $repository | Sort-Object Version -Descending | Select-Object -First 1 if ($latestModule.Version -gt $installedModule.Version) { # An update is available Write-Host "Updating $moduleName to version $($latestModule.Version)..." Update-Module -Name $moduleName -Force -Scope CurrentUser Write-Host "$moduleName has been updated to version $($latestModule.Version)." } else { Write-Host "$moduleName is already up to date (Version $($installedModule.Version))." } } else { # The module is not installed Write-Host "$moduleName is not installed. Installing it..." Install-Module -Name $moduleName -Repository $repository -Scope CurrentUser -Force Write-Host "$moduleName has been installed." } } function New-AzAuthentication { param( [string]$subscriptionId ) Show-Title -title "Authenticate with Azure" -color "Yellow" if ($null -ne $env:AGENT_ID -and $null -ne $env:SYSTEM_TASKDEFINITIONSURI) { # Having the $env:AGENT_ID and $env:SYSTEM_TASKDEFINITIONSURI variables set means we are running in Azure Pipeline. # Authentication with service principal is done by the AzureCli task in Azure Pipeline Write-Host "Using service principal authentication" } else { # User login Write-Host "Using user authentication" $currentSubscriptionId = az account show --query "id" -o tsv if ($currentSubscriptionId -ne $subscriptionId) { az login | Out-Null } } az account set --subscription $subscriptionId | Out-Null $azAccountName = az account show --query 'name' --output tsv Set-AzContext -SubscriptionName $azAccountName Write-Host "Authenticated!" -ForegroundColor "Green" } function Get-Scripts { param( [bool]$delta, [string]$manifestPath, [string]$stack, [string]$environment ) $manifest = Get-Content -Raw -Path $manifestPath | ConvertFrom-Json if ($delta) { $changedScripts = Get-Changed -stack $stack -environment $environment $scripts = $manifest.deploy.scripts | Where-Object { $_ -in $changedScripts } } else { $scripts = $manifest.deploy.scripts } return $scripts } function Get-Changed { param( [string]$type = 'deploy', [string]$stack, [string]$environment ) # Get the root folder of your Git repository $repoRoot = git rev-parse --show-toplevel # Get the commit hashes for the current and previous commits #----------------------------------------------------------------------------------- $currentCommit = git rev-parse HEAD $previousCommit = git rev-parse HEAD^ # Get previous tag instead of commit -1 # Define the prefix of the Git tag you want to search for $tagPrefix = "$stack-$environment*" # Get a list of all tags that start with the specified prefix $matchingTags = git tag --list $tagPrefix # If there are matching tags, find the most recent commit hash among them if ($matchingTags.Count -gt 0) { $previousCommit = $null $mostRecentCommitDate = [datetime]::MinValue foreach ($tag in $matchingTags) { $commitHash = git rev-list -n 1 $tag $commitDate = git show -s --format="%ci" $commitHash | Get-Date if ($commitDate -gt $mostRecentCommitDate) { $mostRecentCommitDate = $commitDate $previousCommit = $commitHash } } } else { Write-Host "No tags found with prefix '$tagPrefix' check if full deploy is needed." exit 1 } #----------------------------------------------------------------------------------- # Get the list of modified files between the current and previous commits $changedFiles = git diff --name-only $previousCommit $currentCommit # Get the distinct folder paths from the changed files $changedFolders = $changedFiles | ForEach-Object { Split-Path -Parent $_ } | Get-Unique # Filter the changed folders to include only the ones containing deploy.ps1 $folder = $changedFolders | Where-Object { Test-Path (Join-Path -Path $repoRoot -ChildPath ($_ + "\$type.ps1")) } # Get the relative paths of deploy.ps1 scripts within the deploy folders and their subfolders $changedScripts = $folder | Get-ChildItem -Recurse -Filter "$type.ps1" -File | ForEach-Object { $_.FullName.Substring($repoRoot.Length + 1) } if ($changedScripts) { # Normalize slashes in paths between operating systems $changedScripts = $changedScripts.Replace('\', '/') } return $changedScripts } function Get-LocationShort { param( [string]$location ) $locations = @{ westeurope = 'weu' } if (!$locations.ContainsKey($location)) { Write-Host "Location '$location' does not have a shortname available" exit 1 } return $locations[$location]; } function Show-Title { param( [string]$title = "", [string]$color = "Cyan", [string]$edge = "=" ) $line = $edge * 75 Write-Host "" Write-Host $line -ForegroundColor $color Write-Host " $title" -ForegroundColor $color Write-Host $line -ForegroundColor $color } function Block-Start { param( [string]$title = "", [string]$color = "Yellow", [string]$edge = "=" ) $separator = $edge * 75 Write-Host $separator Write-Host " $title" -ForegroundColor $color Write-Host "" } function Block-End { param( [string]$title = "", [string]$color = "Green", [string]$edge = "=" ) $separator = $edge * 75 Write-Host "" Write-Host " $title" -ForegroundColor $color Write-Host $separator } function Invoke-Script { param( [string]$scriptPath, [string]$location, [string]$stack, [string]$environment, [string]$subscriptionId ) Show-Title -title "Running $scriptPath ($environment, $stack, $location)" -color "Yellow" try { & $scriptPath -location $location -stack $stack -environment $environment -subscriptionId $subscriptionId Write-Host "Success $scriptPath" -ForegroundColor "Green" } catch { Write-Host "Failed $scriptPath with error: $_" -ForegroundColor "Red" throw $_ } } function Set-GitTag { param( [string]$stack, [string]$environment ) $commitHash = git rev-parse HEAD $timestamp = Get-Date -Format "yy-MM-dd_HH.mm.ss" $tagPrefix = "$stack-$environment" $tag = "$tagPrefix-$timestamp" # Check if a tag already exists on the specified commit $existingTags = git tag -l foreach ($existingTag in $existingTags) { if ($existingTag -like "$tagPrefix*") { # If a tag exists with the specified prefix, remove it git tag -d $existingTag git push --delete origin $existingTag } } # Add the new tag git tag -a $tag -m "Tagging version $tag" $commitHash git push origin $tag } function New-ParameterFile { param( [string]$filename = 'parameter.json', [string]$location = './', [Hashtable]$parametersList ) $parametersObject = @{ '$schema' = "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#" "contentVersion" = "1.0.0.0" "parameters" = @{} } foreach ($paramName in $parametersList.Keys) { $paramValue = $parametersList[$paramName] $parametersObject["parameters"][$paramName] = @{ "value" = $paramValue } } $path = $location + '/' + $filename Write-Host 'Path: '$path $parametersObject | ConvertTo-Json -Depth 100 | Set-Content -Path $path return $path } function Invoke-MainDeploy { param( [bool]$delta, [string]$manifest, [string]$location, [string]$subscriptionId, [string]$stack, [string]$environment ) $scripts = Get-Scripts -delta $delta -manifest $manifest -stack $stack -environment $environment Show-Title -title "Deploying to $location" -color "DarkBlue" Write-Host "Subscription: $subscriptionId" Write-Host "Stack: $stack" Write-Host "Environment: $environment" Write-Host "Scripts to run: $scripts" New-AzAuthentication -SubscriptionId $subscriptionId foreach ($script in $scripts) { Invoke-Script -scriptPath $script ` -location $location ` -locationShort $locationShort ` -environment $environment ` -stack $stack ` -subscriptionId $subscriptionId } Set-GitTag -stack $stack -env $environment } function Push-FunctionCode { param( [string]$csprojAbsolutePath, [string]$csProj, [string]$netVersion = "7.0", [string]$subscriptionId, [string]$resourceGroup, [string]$functionName, [string]$scriptPath ) $functionIsRunning = $false $startTime = Get-Date $timeout = New-TimeSpan -Seconds 300 # Check if function app exist/reachable. do { $functionIsRunning = "Running" -eq (az functionapp show --resource-group $resourceGroup --name $functionName --subscription $subscriptionId --query "state" -o tsv) Write-Host "Function state: $functionIsRunning" if (-not $functionIsRunning) { Start-Sleep -Seconds 5 } if (-not $functionIsRunning -and (Get-Date) -gt ($startTime + $timeout)) { Write-Host "Script execution timed out after 300 seconds." break } } while (-not $functionIsRunning) if (-not $functionIsRunning) { Write-Host "Function App $functionName, not reachable." throw [System.Management.Automation.ItemNotFoundException] } $originLocation = Get-Location # Step 1: Build Adapter Code Set-Location $csprojAbsolutePath $output = dotnet build $csProj -c Release # Step 2: Zip all files $zipFile = Join-Path $scriptPath "$csProj.zip" Get-ChildItem -Path ".\bin\Release\net$netVersion\*" -Force | Compress-Archive -DestinationPath $zipFile -Force Set-Location $scriptPath # Step 3: Deploy Code to Function App az functionapp deployment source config-zip ` --resource-group $resourceGroup ` --name $functionName ` --src $zipFile ` --subscription $subscriptionId Remove-Item -Path $zipFile Set-Location $originLocation } function Get-Configuration { param( [string]$stack, [string]$environment, [string]$locationShort, [string]$cachedConfigFileLocation = "./configuration.json", [int]$cacheExpirationMinutes = 10 ) # Check if cached data exists, is not expired, and has the same stack and environment if ( (Test-Path $cachedConfigFileLocation) -and ((Get-Date) - (Get-Item $cachedConfigFileLocation).LastWriteTime).TotalMinutes -lt $cacheExpirationMinutes -and ((Get-Content -Raw -Path $cachedConfigFileLocation | ConvertFrom-Json).stack -eq $stack) -and ((Get-Content -Raw -Path $cachedConfigFileLocation | ConvertFrom-Json).environment -eq $environment) ) { $cachedData = Get-Content -Path $cachedConfigFileLocation | ConvertFrom-Json -depth 100 Write-Host "Using cached data" return $cachedData } if ($environment -eq "dv") { $apimName = "apim-rdg-iig-dv" $apimRgName = "rg-rpic-iig-rdg-integration-dv-weu" $url = "https://apim-rdg-iig-$environment.azure-api.net/api-rdg-tools-$stack-$environment-$locationShort/v1/GetConfiguration?retailer=$stack&environment=$environment" } else { $apimName = "apim-rpic-iig-$environment" $apimRgName = "rg-rpic-iig-integration-$environment-$locationShort" $url = "https://apim-rpic-iig-$environment.azure-api.net/api-rdg-tools-$stack-$environment-$locationShort/v1/GetConfiguration?retailer=$stack&environment=$environment" } $apimContext = New-AzApiManagementContext -ResourceGroupName $apimRgName -ServiceName $apimName $subscription = Get-AzApiManagementSubscriptionKey -Context $apimContext -SubscriptionId "subscription-rdg-tools-$stack-$environment" # Define headers $headers = @{ "Ocp-Apim-Subscription-Key" = $subscription.PrimaryKey } $response = Invoke-RestMethod -Uri $url -Method Get -Body $requestBody -Headers $headers $response | ConvertTo-Json | Set-Content -Path $cachedConfigFileLocation return $response | ConvertFrom-Json } |