Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Sign PowerShell Scripts and Deploy to Test environment | ||
on: | ||
workflow_dispatch: | ||
push: | ||
env: | ||
ARTIFACT_NAME: PowerShell.Workflows.ScriptSigning | ||
jobs: | ||
sign_scripts: | ||
name: Sign, validate, test and publish PowerShell scripts as pipeline artifacts | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Install PowerShell | ||
uses: microsoft/setup-powershell@v1 | ||
- name: Import code signing certificate | ||
shell: pwsh | ||
run: | | ||
$pfxCertFilePath = Join-Path -Path $PSScriptRoot -ChildPath "CodeSigningCertificate.pfx" | ||
Set-Content -Value $([System.Convert]::FromBase64String($env:BASE64_PFX)) -Path $pfxCertFilePath -Encoding Byte | ||
$codeSigningCert = Import-PfxCertificate -FilePath $pfxCertFilePath -Password $($env:PFX_PASSWORD | ConvertTo-SecureString -AsPlainText -Force) -CertStoreLocation ./cert/codesingingcert/ | ||
env: | ||
BASE64_PFX: ${{ secrets.BASE64_PFX }} | ||
PFX_PASSWORD: ${{ secrets.PFX_PASSWORD }} | ||
- name: Check out repository | ||
uses: actions/checkout@v2 | ||
- name: Sign pwsh scripts and modules | ||
shell: pwsh | ||
run: | | ||
# remove git dir from checked out repo | ||
Get-ChildItem -Path "." -Filter ".git*" -Force | ForEach-Object {Remove-Item -Path $_.FullName -Recurse -Force} | ||
$scripts = Get-ChildItem -Path . -Include *.ps1,*.psm1,*.psd1 -Recurse -ErrorAction Stop | ||
# load cert | ||
$codeSigningCert = Get-ChildItem ./cert/codesingingcert | Select-Object -First 1 | ||
foreach ($script in $scripts) { | ||
try { | ||
$scriptContent = Get-Content -Path $script.FullName | ||
Write-Output "Signing script `"$($script.Name)`" with certificate `"$($codeSigningCert.Thumbprint)`"" | ||
# sign script | ||
$null = Set-AuthenticodeSignature -Certificate $codeSigningCert -FilePath $script.FullName -TimestampServer "http://timestamp.comodoca.com/rfc3161" | ||
} | ||
catch { | ||
Write-Error $_ | ||
} | ||
} | ||
- name: Validate Signature | ||
shell: pwsh | ||
run: | | ||
$signatureStatuses = Get-ChildItem -r -i *.ps* | Get-AuthenticodeSignature | ||
Foreach ($signatureStatus in $signatureStatuses) { | ||
If ($signatureStatus.Status -eq 'HashMismatch') { | ||
throw "File '$($signatureStatus.Path)' has a hash status of '$($signatureStatus.status)'" | ||
} | ||
ElseIf ($signatureStatus.Status -eq 'NotSigned') { | ||
Write-Warning "File '$($signatureStatus.Path)' has a hash status of '$($signatureStatus.status)'" | ||
} | ||
ElseIf ($signatureStatus.Status -eq 'Valid') { | ||
Write-Host "File '$($signatureStatus.Path)' has a hash status of '$($signatureStatus.status)'" | ||
} | ||
Else { | ||
throw "File '$($signatureStatus.Path)' has an unhandled hash status of '$($signatureStatus.status)'" | ||
} | ||
} | ||
- name: Test Module Imports | ||
shell: pwsh | ||
run: | | ||
$ErrorActionPreference = 'Stop' | ||
$moduleFiles = Get-ChildItem -path ./* -recurse -include *.psm1 | ||
Write-Host "Count of module files: $($moduleFiles.count)" | ||
try { | ||
ForEach ($moduleFile in $moduleFiles) { | ||
Import-Module $moduleFile.Fullname -ErrorAction Stop | ||
} | ||
} | ||
catch { | ||
throw "Failed test import module '$moduleFile' with error: $_" | ||
} | ||
$importedModules = Get-Module | ||
Write-Host "Imported modules: `n $($importedModules.Path | Out-String)" | ||
$missingModules = $moduleFiles | Where-object {$_ -inotin ($importedModules).Path} | ||
If ($missingModules) { | ||
throw "The following modules failed import test: $missingModules" | ||
} | ||
- name: Zip Signed Modules | ||
shell: pwsh | ||
run: | | ||
$moduleCodeFilesObjs = Get-ChildItem -Path .\src -Recurse -Include *.psm1 -Exclude '*-GSA*','*GuardrailsSolutionAcceleratorSetup*','*Deploy-GuardrailsSolutionAccelerator*' | ||
Write-Host "'$($moduleCodeFilesObjs.count)' module manifest files " | ||
ForEach ($moduleCodeFile in $moduleCodeFilesObjs) { | ||
$moduleManifestFile = Get-Item -Path $moduleCodeFile.FullName.replace('psm1','psd1') | ||
If ($moduleCodeFilesObjs.FullName -icontains $moduleCodeFile.FullName -or $moduleCodeFilesObjs.FullName -icontains $moduleManifestFile.FullName) { | ||
Write-Host "Module '$($moduleCodeFile.BaseName)' found, zipping module files..." | ||
$destPath = "./psmodules/$($moduleCodeFile.BaseName).zip" | ||
If ($moduleCodeFile.DIrectory.Name -eq 'Guardrails-Localization') { | ||
Compress-Archive -Path "$($moduleCodeFile.Directory)/*" -DestinationPath $destPath -Force | ||
} | ||
Else { | ||
$filesToZip = $moduleManifestFile,$moduleCodeFile | ||
Compress-Archive -Path $filesToZip -DestinationPath $destPath -Force | ||
} | ||
} | ||
Else { | ||
Write-Host "Neither the manifest '$($moduleCodeFile.FullName.toLower())' or script file '$($moduleManifestFile.FullName.ToLower())' for module '$($moduleCodeFile.BaseName)' was changed, skipping zipping..." | ||
} | ||
} | ||
- name: Publish artifacts | ||
uses: actions/upload-artifact@v3 | ||
with: | ||
name: ${{ env.ARTIFACT_NAME }} | ||
path: ./psmodules/*.zip | ||
deploy: | ||
name: Deploy CAC to a tenant | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Check Out | ||
uses: actions/checkout@v3 | ||
- name: Download zipped modules and replace old ones | ||
uses: actions/download-artifact@v3 | ||
with: | ||
name: ${{ env.ARTIFACT_NAME }} | ||
path: ./psmodules | ||
- name: Login to Azure | ||
uses: azure/login@v1 | ||
with: | ||
creds: ${{ secrets.AZURE_CREDENTIALS }} | ||
enable-AzPSSession: true | ||
- name: Stage zipped/signed modules in Storage Account | ||
uses: azure/powershell@v1 | ||
with: | ||
inlineScript: | | ||
$storageContext = (Get-AzStorageAccount -ResourceGroupName $env:PIPELINEMODULESTAGING_RGNAME -Name $env:PIPELINEMODULESTAGING_STORAGEACCOUNTNAME).Context | ||
$zippedModules = Get-ChildItem -Path ./psmodules/* -Include *.zip -File | ||
ForEach ($moduleZip in $zippedModules) { | ||
Set-AzStorageBlobContent -Context $storageContext -Container psmodules -File $moduleZip.FullName -Blob $moduleZip.Name -Force -ErrorAction Stop | ||
} | ||
azPSVersion: "latest" | ||
env: | ||
PIPELINEMODULESTAGING_RGNAME: ${{ vars.PIPELINEMODULESTAGING_RGNAME }} | ||
Check failure on line 149 in .github/workflows/sign-scripts-development.yml GitHub Actions / Sign PowerShell Scripts and Deploy to Test environmentInvalid workflow file
|
||
PIPELINEMODULESTAGING_STORAGEACCOUNTNAME: ${{ vars.ORGANIZATION_VAR }} | ||
- name: Pre-Clean Test environment | ||
uses: azure/powershell@v1 | ||
with: | ||
inlineScript: | | ||
ipmo ./src/GuardrailsSolutionAcceleratorSetup | ||
$configFilePath = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath 'config.json' | ||
$configContent = @' | ||
{ | ||
"keyVaultName": "gsapipe", | ||
"resourcegroup": "gsapipe", | ||
"region": "CanadaCentral", | ||
"storageaccountName": "gsapipe", | ||
"logAnalyticsworkspaceName": "gsapipe", | ||
"autoMationAccountName": "gsapipe", | ||
"FirstBreakGlassAccountUPN": "breakglass1@$env:TESTTENANT_DOMAIN", | ||
"SecondBreakGlassAccountUPN": "breakglass2@$env:TESTTENANT_DOMAIN", | ||
"PBMMPolicyID": "4c4a5f27-de81-430b-b4e5-9cbd50595a87", | ||
"AllowedLocationPolicyId": "e56962a6-4747-49cd-b67b-bf8b01975c4c", | ||
"DepartmentNumber": "190", | ||
"CBSSubscriptionName": "$env:CBSSUBSCRIPTION_NAME", | ||
"SecurityLAWResourceId": "/subscriptions/$env:TESTSUBSCRIPTION_ID/resourceGroups/rg-core/providers/Microsoft.OperationalInsights/workspaces/mtb-law01", | ||
"HealthLAWResourceId": "/subscriptions/$env:TESTSUBSCRIPTION_ID/resourceGroups/rg-core/providers/Microsoft.OperationalInsights/workspaces/mtb-law01", | ||
"Locale": "en-CA", | ||
"lighthouseServiceProviderTenantID": "$env:LIGHTHOUSEPROVIDER_TENANTID", | ||
"lighthousePrincipalDisplayName": "SSC CSPM TEAM", | ||
"lighthousePrincipalId": "$env:LIGHTHOUSEPROVIDER_PRINCIPALID", | ||
"lighthouseTargetManagementGroupID": "mb_co", | ||
"subscriptionId": "$env:TESTSUBSCRIPTION_ID", | ||
"SSCReadOnlyServicePrincipalNameAPPID": "00000000-0000-0000-0000-000000000000", | ||
"uniqueNameSuffix": "$env:UNIQUENAME_SUFFIX", | ||
"securityRetentionDays": "730", | ||
"cloudUsageProfiles": "1,2" | ||
} | ||
'@ | ||
Set-Content -Path $configFilePath -Value $configContent | ||
Push-Location -Path setup | ||
try { | ||
$ErrorActionPreference = 'Stop' | ||
remove-gsacentralizedReportingCustomerComponents -Force -configFilePath $configFilePath | ||
Remove-GSACentralizedDefenderCustomerComponents -Force -configFilePath $configFilePath | ||
Remove-GSACoreResources -Force -Wait -configFilePath $configFilePath | ||
} | ||
catch { | ||
throw "Failed test deploy of solution with error: $_" | ||
} | ||
finally { | ||
If (!$?) {throw "Failed test deploy of solution with error: $($error[0]) $_"} | ||
Pop-Location | ||
} | ||
azPSVersion: "latest" | ||
env: | ||
TESTTENANT_DOMAIN: ${{ vars.TESTTENANT_DOMAIN }} | ||
TESTSUBSCRIPTION_ID: ${{ vars.TESTSUBSCRIPTION_ID }} | ||
CBSSUBSCRIPTION_NAME: ${{ vars.CBSSUBSCRIPTION_NAME }} | ||
TESTSUBSCRIPTION_ID: ${{ vars.TESTSUBSCRIPTION_ID }} | ||
LIGHTHOUSEPROVIDER_TENANTID: ${{ vars.LIGHTHOUSEPROVIDER_TENANTID }} | ||
LIGHTHOUSEPROVIDER_PRINCIPALID: ${{ vars.LIGHTHOUSEPROVIDER_PRINCIPALID }} | ||
UNIQUENAME_SUFFIX: ${{ vars.UNIQUENAME_SUFFIX }} | ||
- name: Deploy Test environment | ||
uses: azure/powershell@v1 | ||
with: | ||
inlineScript: | | ||
ipmo ./src/GuardrailsSolutionAcceleratorSetup | ||
$c = Get-GSAExportedConfig -KeyVaultName gsapipe-$env:UNIQUENAME_SUFFIX -y | ||
$config = $c.configString | ConvertFrom-Json | ||
Write-Output "Waiting for 'main' and 'backend' runbook jobs to complete (up to 5 mins)" | ||
$timeout = New-TimeSpan -Minutes 5 | ||
$timer = [System.Diagnostics.Stopwatch]::StartNew() | ||
do { | ||
$jobMain = Get-AzAutomationJob -RunbookName 'main' -ResourceGroupName $config.runtime.resourceGroup -AutomationAccountName $config.runtime.automationAccountName | | ||
Sort-Object StartTIme -Descending | | ||
Select-Object -First 1 | ||
$jobBackend = Get-AzAutomationJob -RunbookName 'backend' -ResourceGroupName $config.runtime.resourceGroup -AutomationAccountName $config.runtime.automationAccountName | | ||
Sort-Object StartTIme -Descending | | ||
Select-Object -First 1 | ||
Start-Sleep 1 | ||
} | ||
until (($jobMain.Status -in 'Completed','Failed' -and $jobBackend -in 'Completed','Failed') -or ($timer.Elapsed -ge $timeout)) | ||
If ($jobMain.Status -eq 'Failed') { | ||
throw "main runbook failed to execute" | ||
} | ||
If ($jobMain.Status -eq 'Completed') { | ||
Write-Output "'main' runbook completed successfully, checking for errors in output. " | ||
} | ||
If ($jobBackend.Status -eq 'Failed') { | ||
throw "backend runbook failed to execute" | ||
} | ||
If ($jobBackend.Status -eq 'Completed') { | ||
Write-Output "'backend' runbook completed successfully, checking for errors in output. " | ||
} | ||
$jobMainOutput = Get-AzAutomationJobOutput -Id $jobMain.JobId -ResourceGroupName $config.runtime.resourceGroup -AutomationAccountName $config.runtime.automationAccountName -Stream 'Error' | ||
$jobBackendOutput = Get-AzAutomationJobOutput -Id $jobBackend.JobId -ResourceGroupName $config.runtime.resourceGroup -AutomationAccountName $config.runtime.automationAccountName -Stream 'Error' | ||
$errorsFound = $false | ||
ForEach ($outputRecord in $jobMainOutput) { | ||
If ($outputRecord.Summary -like 'Failed invoke the module execution script for module*') { | ||
throw 'Errors found in "main" runbook Azure Automation jobs' | ||
} | ||
} | ||
ForEach ($outputRecord in $jobBackendOutput) { | ||
If ($outputRecord.Summary -like 'Failed invoke the module execution script for module*') { | ||
throw 'Errors found in "backend" runbook Azure Automation jobs' | ||
} | ||
} | ||
azPSVersion: "latest" | ||
env: | ||
UNIQUENAME_SUFFIX: ${{ vars.UNIQUENAME_SUFFIX }} | ||