├── .azure-pipelines ├── pipeline.ci.bicep.yml ├── pipeline.ci.terraform.yml ├── pipeline.destroy.bicep.yml ├── pipeline.destroy.terraform.yml ├── template.bicep.destroy.yml ├── template.bicep.previewdeploy.yml ├── template.bicep.test.yml ├── template.bicep.validate.yml ├── template.terraform.destroy.yml ├── template.terraform.previewdeploy.yml ├── template.terraform.report.yml ├── template.terraform.test.yml └── template.terraform.validate.yml ├── .devcontainer ├── Dockerfile ├── devcontainer.env ├── devcontainer.json └── library-scripts │ ├── azcli-debian.sh │ ├── common-debian.sh │ ├── docker-debian.sh │ ├── go-debian.sh │ └── terraform-debian.sh ├── .editorconfig ├── .gitattributes ├── .github ├── CODEOWNERS ├── dependabot.yml ├── linters │ ├── .gitleaks.toml │ ├── .jsonlintrc.json │ ├── .lychee.toml │ ├── .markdownlint.yml │ ├── .prettierrc.yml │ └── .yamllint.yml └── workflows │ ├── template.bicep.cleanup.yml │ ├── template.bicep.destroy.yml │ ├── template.bicep.previewdeploy.yml │ ├── template.bicep.test.yml │ ├── template.bicep.validate.yml │ ├── template.queryevents.yml │ ├── template.storeevent.yml │ ├── template.terraform.cleanup.yml │ ├── template.terraform.destroy.yml │ ├── template.terraform.previewdeploy.yml │ ├── template.terraform.report.yml │ ├── template.terraform.test.yml │ ├── template.terraform.validate.yml │ ├── workflow.ci.bicep.yml │ ├── workflow.ci.terraform.yml │ ├── workflow.cleanup.bicep.yml │ ├── workflow.cleanup.terraform.yml │ ├── workflow.destroy.bicep.yml │ ├── workflow.destroy.terraform.yml │ ├── workflow.pr.bicep.cleanup.yml │ ├── workflow.pr.bicep.yml │ ├── workflow.pr.checker.yml │ ├── workflow.pr.terraform.cleanup.yml │ └── workflow.pr.terraform.yml ├── .gitignore ├── .gitleaks.toml ├── .lycheeignore ├── .mega-linter.yml ├── .vscode └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── IAC ├── Bicep │ ├── bicep │ │ ├── 01_storage │ │ │ ├── 01_rg │ │ │ │ └── main.bicep │ │ │ └── 02_deployment │ │ │ │ ├── main.bicep │ │ │ │ └── modules │ │ │ │ └── storageAccount.bicep │ │ ├── 02_config │ │ │ ├── 01_rg │ │ │ │ └── main.bicep │ │ │ └── 02_deployment │ │ │ │ ├── _events.sh │ │ │ │ ├── main.bicep │ │ │ │ └── modules │ │ │ │ └── appconfig.bicep │ │ └── modules │ │ │ ├── nameGenerator.bicep │ │ │ ├── nameGeneratorSubscription.bicep │ │ │ ├── randomGenerator.bicep │ │ │ └── resourceGroup.bicep │ └── test │ │ └── end_to_end │ │ └── End_To_End.Tests.ps1 └── Terraform │ ├── .gitignore │ ├── .tflint.hcl │ ├── terraform │ ├── 01_init │ │ ├── main.tf │ │ ├── output.tf │ │ ├── provider.tf │ │ └── variables.tf │ ├── 02_storage │ │ └── 01_deployment │ │ │ ├── _events.sh │ │ │ ├── main.tf │ │ │ ├── output.tf │ │ │ ├── provider.tf │ │ │ └── variables.tf │ └── 03_config │ │ └── 01_deployment │ │ ├── _events.sh │ │ ├── main.tf │ │ ├── output.tf │ │ ├── provider.tf │ │ └── variables.tf │ └── test │ ├── go.mod │ ├── go.sum │ └── terraform │ ├── 01_init_unit_test.go │ ├── 02_storage_unit_test.go │ ├── 03_config_unit_test.go │ ├── end_to_end_test.go │ └── mocked_deployment.tfstate ├── LICENSE ├── README.md ├── SECURITY.md ├── SUPPORT.md ├── docs ├── DEVELOPER_EXPERIENCE.md ├── ENVIRONMENT.md ├── GETTING_STARTED.md ├── GETTING_STARTED_VM.md ├── SAMPLE_APP_ARCHITECTURE.md ├── TESTING.md ├── WORKFLOW.md └── images │ ├── PR_workflow.drawio │ ├── destroy_workflow.png │ ├── environment.PNG │ ├── pr_workflow.png │ ├── symphony.drawio │ └── workflow.png ├── env ├── bicep │ ├── dev │ │ ├── 01_storage │ │ │ ├── 01_rg │ │ │ │ └── parameters.bicepparam │ │ │ └── 02_deployment │ │ │ │ └── parameters.bicepparam │ │ └── 02_config │ │ │ ├── 01_rg │ │ │ └── parameters.bicepparam │ │ │ └── 02_deployment │ │ │ └── parameters.bicepparam │ ├── pr │ │ ├── 01_storage │ │ │ ├── 01_rg │ │ │ │ └── parameters.bicepparam │ │ │ └── 02_deployment │ │ │ │ └── parameters.bicepparam │ │ └── 02_config │ │ │ ├── 01_rg │ │ │ └── parameters.bicepparam │ │ │ └── 02_deployment │ │ │ └── parameters.bicepparam │ └── prod │ │ ├── 01_storage │ │ ├── 01_rg │ │ │ └── parameters.bicepparam │ │ └── 02_deployment │ │ │ └── parameters.bicepparam │ │ └── 02_config │ │ ├── 01_rg │ │ └── parameters.bicepparam │ │ └── 02_deployment │ │ └── parameters.bicepparam └── terraform │ ├── dev │ ├── 01_init.tfvars.json │ ├── 02_storage_01_deployment.tfvars.json │ └── 03_config_01_deployment.tfvars.json │ ├── pr │ ├── 01_init.tfvars.json │ ├── 02_storage_01_deployment.tfvars.json │ └── 03_config_01_deployment.tfvars.json │ └── prod │ ├── 01_init.tfvars.json │ ├── 02_storage_01_deployment.tfvars.json │ └── 03_config_01_deployment.tfvars.json ├── install.sh ├── scripts ├── .gitkeep ├── install │ ├── banner.sh │ ├── cli │ │ └── symphony │ ├── contents.sh │ ├── providers │ │ ├── azdo │ │ │ ├── azdo.sh │ │ │ └── templates │ │ │ │ ├── authorized-resources.json │ │ │ │ ├── pipeline-authorize.json │ │ │ │ ├── pipeline-create.json │ │ │ │ ├── pipeline-variable.json │ │ │ │ ├── project-create.json │ │ │ │ ├── sc-ado-auth.json │ │ │ │ ├── sc-ado-paas-federated.json │ │ │ │ ├── sc-ado-paas.json │ │ │ │ ├── sc-ado-server.json │ │ │ │ ├── service-connection-create-paas.json │ │ │ │ ├── service-connection-create-server.json │ │ │ │ └── variable-group-create.json │ │ └── github │ │ │ └── github.sh │ └── provision.sh ├── orchestrators │ ├── _helpers.sh │ ├── _setup_helpers.sh │ ├── events.sh │ ├── git.diff.sh │ ├── iac.bicep.destroy.sh │ ├── iac.bicep.lint.sh │ ├── iac.bicep.previewdeploy.sh │ ├── iac.bicep.sh │ ├── iac.bicep.validate.sh │ ├── iac.tf.destroy.sh │ ├── iac.tf.lint.sh │ ├── iac.tf.previewdeploy.sh │ ├── iac.tf.sh │ ├── iac.tf.statebackup.sh │ ├── iac.tf.test.sh │ ├── iac.tf.validate.sh │ ├── scanners.sh │ ├── setup-age.sh │ ├── setup-armttk.sh │ ├── setup-azcli.sh │ ├── setup-azpowershell.sh │ ├── setup-benchpress.ps1 │ ├── setup-benchpress.sh │ ├── setup-bicep.sh │ ├── setup-gitleaks.sh │ ├── setup-go.sh │ ├── setup-pester.sh │ ├── setup-powershell.sh │ ├── setup-sops.sh │ ├── setup-terraform.sh │ ├── setup-tflint.sh │ └── tests.runner.sh └── utilities │ ├── http.sh │ ├── json.sh │ ├── os.sh │ ├── pipeline_logger.sh │ ├── service_principal.sh │ ├── shell_inputs.sh │ └── shell_logger.sh └── setup.sh /.azure-pipelines/pipeline.ci.bicep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: $(BuildDefinitionName).$(DayOfYear)$(Rev:.r) 3 | 4 | trigger: 5 | - none 6 | 7 | pr: 8 | - none 9 | 10 | variables: 11 | #=============================================================# 12 | # The following Variables should be set on the pipeline level # 13 | #=============================================================# 14 | 15 | - name: agentImage 16 | value: "ubuntu-latest" 17 | # Commented out to use the values from the Azdo pipeline variables 18 | # - name: environmentName 19 | # value: "dev" 20 | 21 | # - name: locationName 22 | # value: "westus" 23 | 24 | - name: excludedFolders 25 | value: "," 26 | 27 | pool: 28 | vmImage: $(agentImage) 29 | 30 | jobs: 31 | - template: ./template.bicep.validate.yml 32 | parameters: 33 | environmentName: $(environmentName) 34 | locationName: $(locationName) 35 | excludedFolders: $(excludedFolders) 36 | 37 | - template: ./template.bicep.previewdeploy.yml 38 | parameters: 39 | environmentName: $(environmentName) 40 | locationName: $(locationName) 41 | excludedFolders: $(excludedFolders) 42 | 43 | - template: ./template.bicep.test.yml 44 | parameters: 45 | environmentName: $(environmentName) 46 | locationName: $(locationName) 47 | excludedFolders: $(excludedFolders) 48 | -------------------------------------------------------------------------------- /.azure-pipelines/pipeline.ci.terraform.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: $(BuildDefinitionName).$(DayOfYear)$(Rev:.r) 3 | 4 | trigger: 5 | - none 6 | 7 | pr: 8 | - none 9 | 10 | variables: 11 | - group: symphony 12 | 13 | - name: agentImage 14 | value: "ubuntu-latest" 15 | 16 | - name: workDir 17 | value: "$(System.DefaultWorkingDirectory)/IAC/Terraform/terraform" 18 | #=============================================================# 19 | # The following Variables should be set on the pipeline level # 20 | #=============================================================# 21 | 22 | # Commented out to use the values from the Azdo pipeline variables 23 | # Name of the Environment 24 | # - name: environmentName 25 | # value: "dev" 26 | 27 | # Go Lang version 28 | # - name: goVersion 29 | # value: "1.18.1" 30 | 31 | # Terraform version 32 | # - name: terraformVersion 33 | # value: "1.11.0" 34 | # Run Layer tests 35 | - name: layerTestEnabled 36 | value: $(runLayerTest) 37 | 38 | # Backup remote state 39 | - name: backupStateEnabled 40 | value: $(runBackupState) 41 | 42 | pool: 43 | vmImage: $(agentImage) 44 | 45 | stages: 46 | - stage: Validate 47 | displayName: "Validate Configuration" 48 | jobs: 49 | - template: ./template.terraform.validate.yml 50 | parameters: 51 | goVersion: "$(goVersion)" 52 | terraformVersion: "$(terraformVersion)" 53 | 54 | - template: ./template.terraform.previewdeploy.yml 55 | parameters: 56 | goVersion: "$(goVersion)" 57 | terraformVersion: "$(terraformVersion)" 58 | 59 | - template: ./template.terraform.test.yml 60 | parameters: 61 | goVersion: "$(goVersion)" 62 | terraformVersion: "$(terraformVersion)" 63 | 64 | - template: ./template.terraform.report.yml 65 | -------------------------------------------------------------------------------- /.azure-pipelines/pipeline.destroy.bicep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: $(BuildDefinitionName).$(DayOfYear)$(Rev:.r) 3 | 4 | trigger: 5 | - none 6 | 7 | pr: 8 | - none 9 | 10 | variables: 11 | #=============================================================# 12 | # The following Variables should be set on the pipeline level # 13 | #=============================================================# 14 | 15 | # Name of the Agent Pool to use 16 | - name: agentImage 17 | value: "ubuntu-latest" 18 | 19 | # Commented out to use the values from the Azdo pipeline variables 20 | # Name of the Environment 21 | # - name: environmentName 22 | # value: "dev" 23 | 24 | # - name: locationName 25 | # value: "westus" 26 | 27 | 28 | pool: 29 | vmImage: $(agentImage) 30 | 31 | jobs: 32 | - template: ./template.bicep.destroy.yml 33 | parameters: 34 | environmentName: $(environmentName) 35 | locationName: $(locationName) 36 | -------------------------------------------------------------------------------- /.azure-pipelines/pipeline.destroy.terraform.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: $(BuildDefinitionName).$(DayOfYear)$(Rev:.r) 3 | 4 | trigger: 5 | - none 6 | 7 | pr: 8 | - none 9 | 10 | variables: 11 | - group: symphony 12 | 13 | - name: agentImage 14 | value: "ubuntu-latest" 15 | 16 | # Commented out to use the values from the Azdo pipeline variables 17 | # - name: environmentName 18 | # value: "dev" 19 | 20 | pool: 21 | vmImage: $(agentImage) 22 | 23 | jobs: 24 | - template: ./template.terraform.destroy.yml 25 | parameters: 26 | environmentName: $(environmentName) 27 | -------------------------------------------------------------------------------- /.azure-pipelines/template.bicep.destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | parameters: 3 | - name: environmentName 4 | type: string 5 | - name: locationName 6 | type: string 7 | 8 | jobs: 9 | - job: Destroy 10 | displayName: "Destroy" 11 | workspace: 12 | clean: resources 13 | 14 | steps: 15 | - checkout: self 16 | 17 | - task: AzureCLI@2 18 | displayName: 'Destroy environment' 19 | inputs: 20 | azureSubscription: symphony 21 | scriptType: 'bash' 22 | scriptLocation: 'inlineScript' 23 | addSpnToEnvironment: true 24 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 25 | inlineScript: | 26 | export WORKSPACE_PATH="$(System.DefaultWorkingDirectory)" 27 | export RUN_ID="$(Build.BuildId)" 28 | export ENVIRONMENT_NAME="${{ parameters.environmentName }}" 29 | export LOCATION_NAME="${{ parameters.locationName }}" 30 | 31 | ./iac.bicep.destroy.sh 32 | -------------------------------------------------------------------------------- /.azure-pipelines/template.bicep.previewdeploy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | parameters: 3 | - name: environmentName 4 | type: string 5 | - name: locationName 6 | type: string 7 | - name: excludedFolders 8 | type: string 9 | 10 | jobs: 11 | - job: PreviewDeploy 12 | displayName: "Preview and Deploy" 13 | dependsOn: 14 | - Validate 15 | 16 | steps: 17 | - checkout: self 18 | 19 | - task: AzureCLI@2 20 | displayName: 'Run bicep preview and deploy' 21 | inputs: 22 | azureSubscription: symphony 23 | scriptType: 'bash' 24 | scriptLocation: 'inlineScript' 25 | addSpnToEnvironment: true 26 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 27 | inlineScript: | 28 | export WORKSPACE_PATH="$(System.DefaultWorkingDirectory)" 29 | export RUN_ID="$(Build.BuildId)" 30 | export ENVIRONMENT_NAME="${{ parameters.environmentName }}" 31 | export LOCATION_NAME="${{ parameters.locationName }}" 32 | 33 | ./iac.bicep.previewdeploy.sh 34 | 35 | - task: Cache@2 36 | inputs: 37 | path: "$(System.DefaultWorkingDirectory)/IAC/Bicep/bicep" 38 | key: '"$(Build.BuildId)"' 39 | -------------------------------------------------------------------------------- /.azure-pipelines/template.bicep.test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | parameters: 3 | - name: environmentName 4 | type: string 5 | - name: locationName 6 | type: string 7 | - name: excludedFolders 8 | type: string 9 | 10 | jobs: 11 | - job: Test 12 | variables: 13 | - name: bicepJson 14 | value: $[ dependencies.PreviewDeploy.outputs['BicepPreviewDeploy.bicepJson'] ] 15 | displayName: "E2E Test" 16 | dependsOn: 17 | - PreviewDeploy 18 | workspace: 19 | clean: resources 20 | 21 | steps: 22 | - checkout: self 23 | 24 | - task: Bash@3 25 | displayName: "Install required tools" 26 | inputs: 27 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 28 | bashEnvValue: "~/.profile" 29 | failOnStderr: false 30 | targetType: "inline" 31 | script: | 32 | set -e 33 | ./setup-azcli.sh 34 | ./setup-powershell.sh 35 | ./setup-pester.sh 36 | ./setup-benchpress.sh 37 | ./setup-azpowershell.sh 38 | 39 | - task: Cache@2 40 | inputs: 41 | path: "$(System.DefaultWorkingDirectory)/IAC/Bicep/bicep" 42 | key: '"$(Build.BuildId)"' 43 | 44 | - task: AzurePowerShell@5 45 | displayName: 'Run E2E tests' 46 | inputs: 47 | azureSubscription: symphony 48 | ScriptType: 'InlineScript' 49 | pwsh: true 50 | azurePowerShellVersion: 'LatestVersion' 51 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 52 | Inline: | 53 | $bashScript = @" 54 | export WORKSPACE_PATH="$(System.DefaultWorkingDirectory)" 55 | export RUN_ID="$(Build.BuildId)" 56 | export ENVIRONMENT_NAME="${{ parameters.environmentName }}" 57 | export LOCATION_NAME="${{ parameters.locationName }}" 58 | 59 | source ./tests.runner.sh 60 | 61 | pushd $(System.DefaultWorkingDirectory)/IAC/Bicep/bicep 62 | load_dotenv 63 | popd 64 | 65 | bicep pester 66 | "@ 67 | 68 | bash -c "$bashScript" 69 | 70 | - task: PublishTestResults@2 71 | displayName: "Publish E2E test results" 72 | inputs: 73 | testResultsFormat: "NUnit" 74 | testResultsFiles: "*.xml" 75 | searchFolder: "$(System.DefaultWorkingDirectory)/IAC/Bicep/test/end_to_end" 76 | failTaskOnFailedTests: true 77 | testRunTitle: "E2E Test" 78 | publishRunAttachments: true 79 | mergeTestResults: true 80 | -------------------------------------------------------------------------------- /.azure-pipelines/template.bicep.validate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | parameters: 3 | - name: environmentName 4 | type: string 5 | - name: locationName 6 | type: string 7 | - name: excludedFolders 8 | type: string 9 | 10 | jobs: 11 | - job: Validate 12 | displayName: "Validate" 13 | 14 | steps: 15 | - checkout: self 16 | 17 | - task: Bash@3 18 | displayName: "Install required tools" 19 | inputs: 20 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 21 | bashEnvValue: "~/.profile" 22 | failOnStderr: false 23 | targetType: "inline" 24 | script: | 25 | set -e 26 | ./setup-azcli.sh 27 | ./setup-gitleaks.sh 28 | ./setup-powershell.sh 29 | ./setup-armttk.sh 30 | 31 | - task: Bash@3 32 | displayName: "Run Gitleaks" 33 | inputs: 34 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 35 | targetType: "inline" 36 | script: | 37 | source ./scanners.sh 38 | run_gitleaks './../../' 'sarif' 'info' 'true' 'true' 39 | 40 | - task: PublishBuildArtifacts@1 41 | inputs: 42 | pathToPublish: "$(System.DefaultWorkingDirectory)/scripts/orchestrators/gitleaks-report.sarif" 43 | artifactName: 'CodeAnalysisLogs' 44 | 45 | - task: Bash@3 46 | displayName: "Run linter" 47 | env: 48 | WORKSPACE_PATH: $(System.DefaultWorkingDirectory) 49 | inputs: 50 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 51 | targetType: "filePath" 52 | filePath: "$(System.DefaultWorkingDirectory)/scripts/orchestrators/iac.bicep.lint.sh" 53 | 54 | - task: AzureCLI@2 55 | displayName: 'Run bicep validate' 56 | inputs: 57 | azureSubscription: symphony 58 | scriptType: 'bash' 59 | scriptLocation: 'inlineScript' 60 | addSpnToEnvironment: true 61 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 62 | inlineScript: | 63 | export WORKSPACE_PATH="$(System.DefaultWorkingDirectory)" 64 | export RUN_ID="$(Build.BuildId)" 65 | export ENVIRONMENT_NAME="${{ parameters.environmentName }}" 66 | export LOCATION_NAME="${{ parameters.locationName }}" 67 | 68 | ./iac.bicep.validate.sh 69 | -------------------------------------------------------------------------------- /.azure-pipelines/template.terraform.destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | parameters: 3 | - name: environmentName 4 | type: string 5 | 6 | jobs: 7 | - job: Destroy 8 | displayName: "Destroy" 9 | workspace: 10 | clean: resources 11 | 12 | steps: 13 | - checkout: self 14 | 15 | - task: AzureCLI@2 16 | displayName: 'Destroy environment' 17 | inputs: 18 | azureSubscription: symphony 19 | scriptType: 'bash' 20 | scriptLocation: 'inlineScript' 21 | addSpnToEnvironment: true 22 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 23 | inlineScript: | 24 | export WORKSPACE_PATH="$(System.DefaultWorkingDirectory)" 25 | export STATE_RG="$(stateRg)" 26 | export STATE_STORAGE_ACCOUNT="$(stateStorageAccount)" 27 | export STATE_CONTAINER="$(stateContainer)" 28 | export ENVIRONMENT_NAME="$(environmentName)" 29 | 30 | ./iac.tf.destroy.sh 31 | -------------------------------------------------------------------------------- /.azure-pipelines/template.terraform.previewdeploy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | parameters: 3 | - name: goVersion 4 | type: string 5 | - name: terraformVersion 6 | type: string 7 | 8 | jobs: 9 | - job: PreviewDeploy 10 | displayName: "Preview and Deploy" 11 | workspace: 12 | clean: resources 13 | dependsOn: validate 14 | 15 | steps: 16 | - checkout: self 17 | 18 | - task: Bash@3 19 | displayName: "Install Terraform" 20 | inputs: 21 | filePath: "scripts/orchestrators/setup-terraform.sh" 22 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 23 | arguments: ${{ parameters.terraformVersion }} 24 | bashEnvValue: "~/.profile" 25 | failOnStderr: true 26 | 27 | - task: GoTool@0 28 | inputs: 29 | version: ${{ parameters.goVersion }} 30 | goBin: "$(System.DefaultWorkingDirectory)" 31 | 32 | - script: echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)' 33 | 34 | - task: AzureCLI@2 35 | displayName: 'Run Terraform plan & Apply' 36 | inputs: 37 | azureSubscription: symphony 38 | scriptType: 'bash' 39 | scriptLocation: 'inlineScript' 40 | addSpnToEnvironment: true 41 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 42 | inlineScript: | 43 | export WORKSPACE_PATH="$(System.DefaultWorkingDirectory)" 44 | export STATE_RG="$(stateRg)" 45 | export STATE_STORAGE_ACCOUNT="$(stateStorageAccount)" 46 | export STATE_CONTAINER="$(stateContainer)" 47 | export ENVIRONMENT_NAME="$(environmentName)" 48 | 49 | ./iac.tf.previewdeploy.sh 50 | -------------------------------------------------------------------------------- /.azure-pipelines/template.terraform.report.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | jobs: 4 | - job: Report 5 | displayName: "Report" 6 | workspace: 7 | clean: resources 8 | dependsOn: PreviewDeploy 9 | condition: eq(variables['runBackupState'], 'true') 10 | 11 | steps: 12 | - checkout: self 13 | 14 | - task: AzureCLI@2 15 | displayName: 'backup $(environmentName) state files' 16 | inputs: 17 | azureSubscription: symphony 18 | scriptType: 'bash' 19 | scriptLocation: 'inlineScript' 20 | addSpnToEnvironment: true 21 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 22 | inlineScript: | 23 | export STATE_RG="$(stateRg)" 24 | export STATE_STORAGE_ACCOUNT="$(stateStorageAccount)" 25 | export STATE_CONTAINER="$(stateContainer)" 26 | export ENVIRONMENT_NAME="$(environmentName)" 27 | export STATE_STORAGE_ACCOUNT_BACKUP="$(stateStorageAccountBackup)" 28 | export COMMIT_ID="$(Build.SourceVersion)" 29 | export RUN_ID="$(Build.BuildId)" 30 | 31 | ./iac.tf.statebackup.sh 32 | -------------------------------------------------------------------------------- /.azure-pipelines/template.terraform.test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | parameters: 3 | - name: goVersion 4 | type: string 5 | - name: terraformVersion 6 | type: string 7 | 8 | jobs: 9 | - job: Test 10 | workspace: 11 | clean: resources 12 | displayName: "E2E Test" 13 | dependsOn: PreviewDeploy 14 | 15 | steps: 16 | - checkout: self 17 | 18 | - task: Bash@3 19 | displayName: "Install Terraform" 20 | inputs: 21 | filePath: "scripts/orchestrators/setup-terraform.sh" 22 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 23 | arguments: ${{ parameters.terraformVersion }} 24 | bashEnvValue: "~/.profile" 25 | failOnStderr: true 26 | 27 | - task: GoTool@0 28 | inputs: 29 | version: ${{ parameters.goVersion }} 30 | goBin: "$(System.DefaultWorkingDirectory)" 31 | 32 | - script: echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)' 33 | 34 | - task: AzureCLI@2 35 | displayName: 'Run E2E tests' 36 | inputs: 37 | azureSubscription: symphony 38 | scriptType: 'bash' 39 | scriptLocation: 'inlineScript' 40 | addSpnToEnvironment: true 41 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 42 | inlineScript: | 43 | export WORKSPACE_PATH="$(System.DefaultWorkingDirectory)" 44 | export STATE_RG="$(stateRg)" 45 | export STATE_STORAGE_ACCOUNT="$(stateStorageAccount)" 46 | export STATE_CONTAINER="$(stateContainer)" 47 | export ENVIRONMENT_NAME="$(environmentName)" 48 | export TEST_TAG="" 49 | 50 | ./iac.tf.test.sh 51 | 52 | - task: PublishTestResults@2 53 | displayName: "Publish E2E test results" 54 | inputs: 55 | testResultsFormat: "JUnit" 56 | testResultsFiles: "*.xml" 57 | searchFolder: "$(System.DefaultWorkingDirectory)/IAC/Terraform/test/terraform" 58 | failTaskOnFailedTests: true 59 | testRunTitle: "E2E Test" 60 | publishRunAttachments: true 61 | mergeTestResults: true 62 | -------------------------------------------------------------------------------- /.azure-pipelines/template.terraform.validate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | parameters: 3 | - name: goVersion 4 | type: string 5 | - name: terraformVersion 6 | type: string 7 | 8 | jobs: 9 | - job: Validate 10 | displayName: "Validate" 11 | workspace: 12 | clean: resources 13 | steps: 14 | - checkout: self 15 | 16 | - task: Bash@3 17 | displayName: "Setup Toolset" 18 | inputs: 19 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 20 | arguments: ${{ parameters.terraformVersion }} 21 | bashEnvValue: "~/.profile" 22 | failOnStderr: false 23 | targetType: "inline" 24 | script: | 25 | set -e 26 | ./setup-azcli.sh 27 | ./setup-terraform.sh ${{ parameters.terraformVersion }} 28 | ./setup-tflint.sh 29 | ./setup-gitleaks.sh 30 | 31 | - task: GoTool@0 32 | inputs: 33 | version: ${{ parameters.goVersion }} 34 | goBin: "$(System.DefaultWorkingDirectory)" 35 | 36 | - script: echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)' 37 | 38 | - task: Bash@3 39 | displayName: "Run GitLeaks" 40 | inputs: 41 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 42 | targetType: "inline" 43 | script: | 44 | source ./scanners.sh 45 | run_gitleaks './../../' 'sarif' 'info' 'true' 'true' 46 | 47 | - task: PublishBuildArtifacts@1 48 | inputs: 49 | pathToPublish: "$(System.DefaultWorkingDirectory)/scripts/orchestrators/gitleaks-report.sarif" 50 | artifactName: 'CodeAnalysisLogs' 51 | 52 | - task: Bash@3 53 | displayName: "Run Terraform lint" 54 | env: 55 | WORKSPACE_PATH: $(System.DefaultWorkingDirectory) 56 | inputs: 57 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 58 | targetType: "filePath" 59 | filePath: "scripts/orchestrators/iac.tf.lint.sh" 60 | 61 | - task: AzureCLI@2 62 | displayName: 'Run Terraform validate' 63 | inputs: 64 | azureSubscription: symphony 65 | scriptType: 'bash' 66 | scriptLocation: 'inlineScript' 67 | addSpnToEnvironment: true 68 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 69 | inlineScript: | 70 | export WORKSPACE_PATH="$(System.DefaultWorkingDirectory)" 71 | export STATE_RG="$(stateRg)" 72 | export STATE_STORAGE_ACCOUNT="$(stateStorageAccount)" 73 | export STATE_CONTAINER="$(stateContainer)" 74 | export ENVIRONMENT_NAME="$(environmentName)" 75 | 76 | ./iac.tf.validate.sh 77 | 78 | - task: AzureCLI@2 79 | condition: and(succeeded(), eq(variables['layerTestEnabled'], 'true')) 80 | displayName: 'Run Layers tests' 81 | inputs: 82 | azureSubscription: symphony 83 | scriptType: 'bash' 84 | scriptLocation: 'inlineScript' 85 | addSpnToEnvironment: true 86 | workingDirectory: "$(System.DefaultWorkingDirectory)/scripts/orchestrators" 87 | inlineScript: | 88 | export WORKSPACE_PATH="$(System.DefaultWorkingDirectory)" 89 | export STATE_RG="$(stateRg)" 90 | export STATE_STORAGE_ACCOUNT="$(stateStorageAccount)" 91 | export STATE_CONTAINER="$(stateContainer)" 92 | export ENVIRONMENT_NAME="$(environmentName)" 93 | export TEST_TAG="module_tests" 94 | 95 | ./iac.tf.test.sh 96 | 97 | - task: PublishTestResults@2 98 | condition: and(succeeded(), eq(variables['layerTestEnabled'], 'true')) 99 | displayName: "Publish Layer tests results" 100 | inputs: 101 | testResultsFormat: "JUnit" 102 | testResultsFiles: "*.xml" 103 | searchFolder: "$(System.DefaultWorkingDirectory)/IAC/Terraform/test/terraform" 104 | failTaskOnFailedTests: true 105 | testRunTitle: "Terraform Layers tests" 106 | publishRunAttachments: true 107 | mergeTestResults: true 108 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # You can pick any Debian/Ubuntu-based image. 😊 2 | ARG VARIANT=bullseye 3 | FROM mcr.microsoft.com/vscode/devcontainers/base:${VARIANT} 4 | 5 | COPY library-scripts/*.sh /tmp/library-scripts/ 6 | 7 | # [Option] Install zsh 8 | ARG INSTALL_ZSH="true" 9 | # [Option] Upgrade OS packages to their latest versions 10 | ARG UPGRADE_PACKAGES="false" 11 | # [Option] Enable non-root Docker access in container 12 | ARG ENABLE_NONROOT_DOCKER="true" 13 | # [Option] Use the OSS Moby CLI instead of the licensed Docker CLI 14 | ARG USE_MOBY="true" 15 | # Enable new "BUILDKIT" mode for Docker CLI 16 | ENV DOCKER_BUILDKIT=1 17 | 18 | # Install needed packages and setup non-root user. Use a separate RUN statement to add your own dependencies. 19 | ARG USERNAME=vscode 20 | ARG USER_UID=1000 21 | ARG USER_GID=$USER_UID 22 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 23 | && bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" "true" "true" \ 24 | && apt-get install -y graphviz make 25 | 26 | # [Option] Install Azure CLI 27 | ARG INSTALL_AZURE_CLI="true" 28 | ARG AZURE_CLI_VERSION="" 29 | # [Option] Install Azure Bicep 30 | ARG INSTALL_AZURE_BICEP="true" 31 | ARG AZURE_BICEP_VERSION="" 32 | # [Option] Install Docker CLI 33 | ARG INSTALL_DOCKER="true" 34 | 35 | ENV NVM_DIR=/usr/local/share/nvm 36 | ENV NVM_SYMLINK_CURRENT=true \ 37 | PATH=${NVM_DIR}/current/bin:${PATH} 38 | RUN if [ "${INSTALL_AZURE_CLI}" = "true" ]; then bash /tmp/library-scripts/azcli-debian.sh "${AZURE_CLI_VERSION}"; fi \ 39 | && if [ "${INSTALL_AZURE_BICEP}" = "true" ]; then AZURE_BICEP_VERSION=${AZURE_BICEP_VERSION:-"latest"} && curl -Lo /usr/local/bin/bicep https://github.com/Azure/bicep/releases/download/${AZURE_BICEP_VERSION}/bicep-linux-x64 && chmod +x /usr/local/bin/bicep; fi \ 40 | && if [ "${INSTALL_DOCKER}" = "true" ]; then \ 41 | bash /tmp/library-scripts/docker-debian.sh "${ENABLE_NONROOT_DOCKER}" "/var/run/docker-host.sock" "/var/run/docker.sock" "${USERNAME}"; \ 42 | else \ 43 | echo '#!/bin/bash\n"$@"' > /usr/local/share/docker-init.sh && chmod +x /usr/local/share/docker-init.sh; \ 44 | fi 45 | 46 | # [Option] Install Go tools 47 | ARG INSTALL_GO="true" 48 | ARG GO_VERSION="" 49 | ENV GO111MODULE=auto 50 | RUN if [ "${INSTALL_GO}" = "true" ]; then bash /tmp/library-scripts/go-debian.sh "${GO_VERSION}" "/usr/local/go" "${GOPATH}" "${USERNAME}" "true" \ 51 | && export PATH=/usr/local/go/bin:${PATH} && chmod +x /usr/local/go/bin/go; fi 52 | 53 | # Install Terraform, tflint, Terragrunt 54 | ARG INSTALL_TERRAFORM="true" 55 | ARG TERRAFORM_VERSION="" 56 | ARG TFLINT_VERSION="" 57 | ARG TERRAGRUNT_VERSION="" 58 | RUN if [ "${INSTALL_TERRAFORM}" = "true" ]; then bash /tmp/library-scripts/terraform-debian.sh "${TERRAFORM_VERSION}" "${TFLINT_VERSION}" "${TERRAGRUNT_VERSION}"; fi 59 | 60 | # # Install TFLint Azure RM Ruleset 61 | ARG TFLINT_RULESET_AZURERM_VERSION="" 62 | RUN if [ "${INSTALL_TERRAFORM}" = "true" ]; then curl -Lo ~/tflint-ruleset-azurerm.zip https://github.com/terraform-linters/tflint-ruleset-azurerm/releases/download/${TFLINT_RULESET_AZURERM_VERSION}/tflint-ruleset-azurerm_linux_amd64.zip \ 63 | && mkdir -p ~/.tflint.d/plugins \ 64 | && unzip ~/tflint-ruleset-azurerm.zip -d ~/.tflint.d/plugins \ 65 | && mv ~/.tflint.d /home/vscode \ 66 | && rm -f ~/tflint-ruleset-azurerm.zip; fi 67 | 68 | # [Optional] Uncomment this section to install additional OS packages. 69 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 70 | # && apt-get -y install --no-install-recommends 71 | 72 | # Cleanup 73 | RUN apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts 74 | 75 | ENTRYPOINT [ "/usr/local/share/docker-init.sh" ] 76 | CMD [ "sleep", "infinity" ] 77 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.env: -------------------------------------------------------------------------------- 1 | # the target azure subscription 2 | ARM_SUBSCRIPTION_ID= 3 | 4 | # the name of the resource group where the storage account for the backend should be created 5 | TF_BACKEND_RESOURCE_GROUP= 6 | 7 | # the region where the resource group for the backend should be created 8 | TF_BACKEND_LOCATION= 9 | 10 | # the name of the storage account for the backend 11 | TF_BACKEND_STORAGE_ACCOUNT= 12 | 13 | # the name of the storage account container for the backend 14 | TF_BACKEND_CONTAINER= 15 | 16 | # the name of the storage account container blob for the backend 17 | TF_BACKEND_KEY= 18 | 19 | # Test-Project variable: the resource group name to be provisioned with terraform 20 | # TF_VAR_RG_NAME= 21 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.226.0/containers/azure-terraform 3 | { 4 | "name": "Symphony", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | "args": { 8 | "VARIANT": "bullseye", 9 | "TERRAFORM_VERSION": "1.11.0", 10 | "TFLINT_VERSION": "0.34.1", 11 | "TFLINT_RULESET_AZURERM_VERSION": "v0.14.0", 12 | "TERRAGRUNT_VERSION": "0.36.3", 13 | "AZURE_CLI_VERSION": "2.34.1", 14 | "AZURE_BICEP_VERSION": "v0.4.1318", 15 | "GO_VERSION": "1.17.8", 16 | "INSTALL_DOCKER": "true", 17 | "ENABLE_NONROOT_DOCKER": "true", 18 | "USE_MOBY": "true", 19 | "UPGRADE_PACKAGES": "true", 20 | "INSTALL_ZSH": "true" 21 | } 22 | }, 23 | // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker-host.sock,type=bind" ], 24 | // "mounts": ["source=dind-var-lib-docker,target=/var/lib/docker,type=volume"], 25 | "overrideCommand": false, 26 | "runArgs": [ 27 | "--env-file", 28 | ".devcontainer/devcontainer.env", 29 | "--cap-add=SYS_PTRACE", 30 | "--security-opt", 31 | "seccomp=unconfined" 32 | ], 33 | "remoteEnv": { 34 | // Sets environment variables required for terrafom remote backend 35 | "TF_BACKEND_RESOURCE_GROUP": "${containerEnv:TF_BACKEND_RESOURCE_GROUP}", 36 | "TF_BACKEND_LOCATION": "${containerEnv:TF_BACKEND_LOCATION}", 37 | "TF_BACKEND_STORAGE_ACCOUNT": "${containerEnv:TF_BACKEND_STORAGE_ACCOUNT}", 38 | "TF_BACKEND_CONTAINER": "${containerEnv:TF_BACKEND_CONTAINER}", 39 | "TF_BACKEND_KEY": "${containerEnv:TF_BACKEND_KEY}" 40 | // Use 'TF_VAR' prefix to set input variables for terraform templates 41 | // "TF_VAR_rg_name": "${containerEnv:TF_VAR_RG_NAME}" 42 | }, 43 | // Set *default* container specific settings.json values on container create. 44 | "settings": { 45 | "terraform.languageServer": { 46 | "enabled": true, 47 | "args": [] 48 | }, 49 | "azureTerraform.terminal": "integrated", 50 | "go.toolsManagement.checkForUpdates": "local", 51 | "go.useLanguageServer": true, 52 | "go.gopath": "/go", 53 | "go.goroot": "/usr/local/go" 54 | }, 55 | // Add the IDs of extensions you want installed when the container is created. 56 | "extensions": [ 57 | "hashicorp.terraform", 58 | "ms-azuretools.vscode-azureterraform", 59 | "ms-dotnettools.vscode-dotnet-runtime", 60 | "golang.Go", 61 | "mikestead.dotenv", 62 | "esbenp.prettier-vscode", 63 | "GitHub.vscode-pull-request-github", 64 | "GitHub.codespaces", 65 | "GitHub.copilot", 66 | "GitHub.remotehub", 67 | "GitHub.vscode-codeql", 68 | "ms-vscode.azure-account", 69 | "yzhang.markdown-all-in-one", 70 | "cschleiden.vscode-github-actions", 71 | "ms-vscode-remote.vscode-remote-extensionpack", 72 | "VisualStudioExptTeam.vscodeintellicode", 73 | "ms-vscode.powershell", 74 | "ms-vscode.azure-repos", 75 | "azps-tools.azps-tools", 76 | "ms-vscode.vscode-node-azure-pack", 77 | "eamodio.gitlens", 78 | "tfsec.tfsec", 79 | "bierner.markdown-mermaid" 80 | ], 81 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 82 | // "forwardPorts": [], 83 | // Use 'postCreateCommand' to run commands after the container is created. 84 | // "postCreateCommand": "terraform --version", 85 | // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 86 | "remoteUser": "vscode", 87 | "features": { 88 | "docker-in-docker": "latest", 89 | "docker-from-docker": "latest", 90 | // "terraform": { 91 | // "version": "1.11.0", 92 | // "tflint": "0.34.1", 93 | // "terragrunt": "0.36.3" 94 | // }, 95 | "git": "os-provided", 96 | "github-cli": "latest", 97 | // "azure-cli": "2.34.1", 98 | // "golang": "1.17.8", 99 | "powershell": "7.2.1", 100 | "dotnet": "6.0" 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 2 10 | 11 | [*.{cmd,bat}] 12 | end_of_line = crlf 13 | 14 | [*.{cpp,cs,gradle,java,kt,py,rs}] 15 | indent_size = 4 16 | 17 | [*.tsv] 18 | indent_style = tab 19 | 20 | [Makefile] 21 | indent_style = tab 22 | 23 | # Ignore paths 24 | # [scripts/install/banner.sh] 25 | # indent_size = unset 26 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behavior to automatically normalize line endings. 2 | * text=auto eol=lf 3 | 4 | # Force batch scripts to always use CRLF line endings so that if a repo is accessed 5 | # in Windows via a file share from Linux, the scripts will work. 6 | *.{cmd,[cC][mM][dD]} text eol=crlf 7 | *.{bat,[bB][aA][tT]} text eol=crlf 8 | 9 | # Force bash scripts to always use LF line endings so that if a repo is accessed 10 | # in Unix via a file share from Windows, the scripts will work. 11 | *.sh text eol=lf 12 | 13 | # Common files config 14 | *.jpg -text 15 | *.png -text 16 | *.gif -text 17 | *.mp4 -text 18 | *.pdf -text 19 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-syntax 2 | 3 | * @microsoft/symphony-review 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/dependabot-2.0.json 2 | # Please see the documentation for all configuration options: 3 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 4 | --- 5 | version: 2 6 | updates: 7 | - package-ecosystem: github-actions 8 | directory: / 9 | schedule: 10 | interval: weekly 11 | commit-message: 12 | prefix: ⬆️ github-actions 13 | include: scope 14 | -------------------------------------------------------------------------------- /.github/linters/.jsonlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": true, 3 | "trailing-commas": true, 4 | "duplicate-keys": false, 5 | "log-files": true, 6 | "compact": true, 7 | "continue": true, 8 | "patterns": ["**/*.json", "!**/node_modules"] 9 | } 10 | -------------------------------------------------------------------------------- /.github/linters/.lychee.toml: -------------------------------------------------------------------------------- 1 | # https://lychee.cli.rs/#/usage/config 2 | # Example config: https://github.com/lycheeverse/lychee/blob/master/lychee.example.toml 3 | 4 | 5 | ############################# Cache ############################### 6 | 7 | # Enable link caching. This can be helpful to avoid checking the same links on multiple runs. 8 | cache = true 9 | 10 | # Discard all cached requests older than this duration. 11 | max_cache_age = "1d" 12 | 13 | ############################# Runtime ############################# 14 | 15 | # Maximum number of allowed redirects. 16 | max_redirects = 6 17 | 18 | # Maximum number of allowed retries before a link is declared dead. 19 | max_retries = 2 20 | 21 | # Maximum number of concurrent link checks. 22 | # max_concurrency = 2 23 | 24 | ############################# Requests ############################ 25 | 26 | # User agent to send with each request. 27 | user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.43" 28 | 29 | # Minimum wait time in seconds between retries of failed requests. 30 | retry_wait_time = 2 31 | 32 | # Comma-separated list of accepted status codes for valid links. 33 | accept = [200, 206, 301, 429] 34 | 35 | # Only test links with the given schemes (e.g. https). 36 | # Omit to check links with any scheme. 37 | scheme = ["https", "http", "file"] 38 | 39 | # Custom request headers 40 | headers = ['Accept-Encoding: deflate, compress, gzip, br, zstd'] 41 | 42 | ############################# Exclusions ########################## 43 | 44 | # Ignore case of paths when matching glob patterns. 45 | glob_ignore_case = true 46 | 47 | # Exclude all private IPs from checking. 48 | exclude_all_private = true 49 | 50 | # Exclude private IP address ranges from checking. 51 | exclude_private = true 52 | 53 | # Exclude link-local IP address range from checking. 54 | exclude_link_local = true 55 | 56 | # Exclude loopback IP address range and localhost from checking. 57 | exclude_loopback = true 58 | 59 | # Exclude all mail addresses from checking. 60 | exclude_mail = true 61 | 62 | # Exclude these filesystem paths from getting checked. 63 | exclude_path = ["node_modules", "site", ".venv"] 64 | 65 | # exclude = ['.*\.github.com\.*'] 66 | -------------------------------------------------------------------------------- /.github/linters/.prettierrc.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/prettierrc.json 2 | --- 3 | printWidth: 120 4 | tabWidth: 2 5 | useTabs: false 6 | semi: false 7 | singleQuote: true 8 | quoteProps: 'consistent' 9 | jsxSingleQuote: true 10 | trailingComma: 'none' 11 | bracketSpacing: true 12 | bracketSameLine: false 13 | endOfLine: 'lf' 14 | embeddedLanguageFormatting: 'auto' 15 | arrowParens: 'always' 16 | htmlWhitespaceSensitivity: 'css' 17 | vueIndentScriptAndStyle: true 18 | -------------------------------------------------------------------------------- /.github/linters/.yamllint.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/yamllint.json 2 | # https://yamllint.readthedocs.io/en/stable/configuration.html#extending-the-default-configuration 3 | --- 4 | extends: default 5 | 6 | locale: en_US.UTF-8 7 | 8 | yaml-files: 9 | - ".yamllint.yml" 10 | - "*.yaml" 11 | - "*.yml" 12 | - "*.pages" 13 | 14 | rules: 15 | line-length: disable 16 | comments-indentation: disable 17 | comments: disable 18 | -------------------------------------------------------------------------------- /.github/workflows/template.bicep.cleanup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Template-Bicep-Cleanup" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_call: 6 | inputs: 7 | pullRequestNumber: 8 | type: string 9 | required: true 10 | githubEnvironment: 11 | type: string 12 | required: true 13 | 14 | permissions: 15 | id-token: write 16 | contents: read 17 | 18 | jobs: 19 | GetEnvironments: 20 | name: GetEnvironments 21 | uses: ./.github/workflows/template.queryevents.yml 22 | with: 23 | pipelineName: PR 24 | eventName: NewEnvironment 25 | eventGroupId: PR-${{ inputs.pullRequestNumber }}-${{ github.repository_id }} 26 | githubEnvironment: symphony 27 | 28 | Destroy: 29 | name: Destroy 30 | environment: ${{ inputs.githubEnvironment }} 31 | needs: [GetEnvironments] 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v4 35 | with: 36 | fetch-depth: 0 37 | 38 | - name: Setup Toolset 39 | run: | 40 | ./setup-azcli.sh 41 | source ~/.bashrc 42 | working-directory: scripts/orchestrators 43 | 44 | - uses: azure/login@v2 45 | name: Run Azure Login 46 | with: 47 | client-id: ${{ secrets.AZURE_CLIENT_ID }} 48 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 49 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 50 | 51 | - name: Run Bicep Destroy 52 | env: 53 | WORKSPACE_PATH: ${{ github.workspace }} 54 | RUN_ID: ${{ github.run_id }} 55 | NEW_ENVIRONMENT_EVENTS_JSON: ${{ needs.GetEnvironments.outputs.events }} 56 | run: | 57 | # NEW_ENVIRONMENT_EVENTS_JSON is an array of events. 58 | # Each event has a Name, SHA and Location properties. 59 | # For each event, we want to checkout the .SHA commit, get the .Name environment name 60 | # and the Location of the environment, and pass it to the destroy script. 61 | 62 | for event in $(echo "${NEW_ENVIRONMENT_EVENTS_JSON}" | jq -c '.[]'); do 63 | sha=$(echo "${event}" | jq -r '.SHA') 64 | name=$(echo "${event}" | jq -r '.Name') 65 | location=$(echo "${event}" | jq -r '.Location') 66 | 67 | git checkout "${sha}" 68 | echo "Destroying environment: $name" 69 | export ENVIRONMENT_NAME=$name 70 | export LOCATION_NAME=$location 71 | 72 | ./iac.bicep.destroy.sh 73 | done 74 | 75 | working-directory: scripts/orchestrators 76 | -------------------------------------------------------------------------------- /.github/workflows/template.bicep.destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Template-Bicep-Destroy" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_call: 6 | inputs: 7 | environmentName: 8 | type: string 9 | required: true 10 | locationName: 11 | type: string 12 | required: true 13 | branchName: 14 | required: true 15 | type: string 16 | githubEnvironment: 17 | type: string 18 | required: true 19 | 20 | permissions: 21 | id-token: write 22 | contents: read 23 | 24 | jobs: 25 | Destroy: 26 | name: Destroy 27 | runs-on: ubuntu-latest 28 | environment: ${{ inputs.githubEnvironment }} 29 | steps: 30 | - uses: actions/checkout@v4 31 | with: 32 | ref: ${{ inputs.branchName }} 33 | 34 | - name: Setup Toolset 35 | run: | 36 | ./setup-azcli.sh 37 | 38 | working-directory: scripts/orchestrators 39 | 40 | - uses: azure/login@v2 41 | name: Run Azure Login 42 | with: 43 | client-id: ${{ secrets.AZURE_CLIENT_ID }} 44 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 45 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 46 | 47 | - name: Run Bicep Destroy 48 | env: 49 | ENVIRONMENT_NAME: ${{ inputs.environmentName }} 50 | LOCATION_NAME: ${{ inputs.locationName }} 51 | WORKSPACE_PATH: ${{ github.workspace }} 52 | RUN_ID: ${{ github.run_id }} 53 | run: | 54 | ./iac.bicep.destroy.sh 55 | working-directory: scripts/orchestrators 56 | -------------------------------------------------------------------------------- /.github/workflows/template.bicep.previewdeploy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Template-Bicep-PreviewDeploy" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_call: 6 | inputs: 7 | environmentName: 8 | type: string 9 | required: true 10 | environmentDirectory: 11 | type: string 12 | required: true 13 | locationName: 14 | type: string 15 | required: true 16 | branchName: 17 | required: true 18 | type: string 19 | githubEnvironment: 20 | type: string 21 | required: true 22 | 23 | permissions: 24 | id-token: write 25 | contents: read 26 | 27 | jobs: 28 | PreviewDeploy: 29 | name: "Preview and Deploy" 30 | runs-on: ubuntu-latest 31 | environment: ${{ inputs.githubEnvironment }} 32 | steps: 33 | - uses: actions/checkout@v4 34 | with: 35 | ref: ${{ inputs.branchName }} 36 | 37 | - name: Setup Toolset 38 | run: | 39 | ./setup-azcli.sh 40 | working-directory: scripts/orchestrators 41 | 42 | - uses: azure/login@v2 43 | name: Run Azure Login 44 | with: 45 | client-id: ${{ secrets.AZURE_CLIENT_ID }} 46 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 47 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 48 | 49 | - name: Run Bicep Deploy 50 | id: BicepDeploy 51 | env: 52 | ENVIRONMENT_NAME: ${{ inputs.environmentName }} 53 | ENVIRONMENT_DIRECTORY: ${{ inputs.environmentDirectory }} 54 | LOCATION_NAME: ${{ inputs.locationName }} 55 | WORKSPACE_PATH: ${{ github.workspace }} 56 | RUN_ID: ${{ github.run_id }} 57 | run: | 58 | ./iac.bicep.previewdeploy.sh 59 | working-directory: scripts/orchestrators 60 | 61 | - name: Cache Environment 62 | id: cache-env 63 | uses: actions/cache@v4 64 | with: 65 | path: ${{ github.workspace }}/IAC/Bicep/bicep/.env 66 | key: ${{ runner.os }}-${{ github.run_id }}-bicep-env 67 | -------------------------------------------------------------------------------- /.github/workflows/template.bicep.test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Template-Bicep-Test" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_call: 6 | inputs: 7 | environmentName: 8 | type: string 9 | required: true 10 | locationName: 11 | type: string 12 | required: true 13 | branchName: 14 | required: true 15 | type: string 16 | githubEnvironment: 17 | type: string 18 | required: true 19 | 20 | permissions: 21 | id-token: write 22 | contents: read 23 | issues: read 24 | checks: write 25 | pull-requests: write 26 | 27 | jobs: 28 | Test: 29 | name: Test 30 | runs-on: ubuntu-latest 31 | environment: ${{ inputs.githubEnvironment }} 32 | steps: 33 | - uses: actions/checkout@v4 34 | with: 35 | ref: ${{ inputs.branchName }} 36 | 37 | - name: Setup Toolset 38 | run: | 39 | ./setup-azcli.sh 40 | ./setup-powershell.sh 41 | ./setup-pester.sh 42 | ./setup-benchpress.sh 43 | ./setup-azpowershell.sh 44 | source ~/.bashrc 45 | working-directory: scripts/orchestrators 46 | 47 | - uses: azure/login@v2 48 | name: Run Azure Login 49 | with: 50 | client-id: ${{ secrets.AZURE_CLIENT_ID }} 51 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 52 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 53 | enable-AzPSSession: true 54 | 55 | - name: Fetch Cached Environment 56 | id: cache-env 57 | uses: actions/cache@v4 58 | with: 59 | path: ${{ github.workspace }}/IAC/Bicep/bicep/.env 60 | key: ${{ runner.os }}-${{ github.run_id }}-bicep-env 61 | 62 | - name: Run e2e tests 63 | env: 64 | ENVIRONMENT_NAME: ${{ inputs.environmentName }} 65 | LOCATION_NAME: ${{ inputs.locationName }} 66 | WORKSPACE_PATH: ${{ github.workspace }} 67 | RUN_ID: ${{ github.run_id }} 68 | run: | 69 | source ./tests.runner.sh 70 | 71 | pushd ../../IAC/Bicep/bicep 72 | load_dotenv 73 | popd 74 | 75 | bicep pester 76 | working-directory: scripts/orchestrators 77 | 78 | - name: Upload E2E Test Results 79 | uses: actions/upload-artifact@v4 80 | if: always() 81 | with: 82 | name: e2e-test--results 83 | path: IAC/Bicep/test/end_to_end 84 | 85 | - name: Publish E2E Test Results 86 | uses: EnricoMi/publish-unit-test-result-action@v2 87 | if: always() 88 | with: 89 | nunit_files: "./IAC/Bicep/test/end_to_end/*.xml" 90 | check_name: "E2E Tests Results" 91 | action_fail: true 92 | -------------------------------------------------------------------------------- /.github/workflows/template.bicep.validate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Template-Bicep-Validate" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_call: 6 | inputs: 7 | environmentName: 8 | type: string 9 | required: true 10 | environmentDirectory: 11 | type: string 12 | required: true 13 | locationName: 14 | type: string 15 | required: true 16 | branchName: 17 | required: true 18 | type: string 19 | githubEnvironment: 20 | type: string 21 | required: true 22 | 23 | permissions: 24 | id-token: write 25 | contents: read 26 | actions: read 27 | security-events: write 28 | 29 | jobs: 30 | Validate: 31 | name: Validate 32 | runs-on: ubuntu-latest 33 | environment: ${{ inputs.githubEnvironment }} 34 | steps: 35 | - uses: actions/checkout@v4 36 | with: 37 | ref: ${{ inputs.branchName }} 38 | 39 | - name: Setup Toolset 40 | run: | 41 | ./setup-azcli.sh 42 | ./setup-gitleaks.sh 43 | ./setup-powershell.sh 44 | ./setup-pester.sh 45 | ./setup-armttk.sh 46 | source ~/.bashrc 47 | working-directory: scripts/orchestrators 48 | 49 | - name: Run Gitleaks 50 | run: | 51 | source ./scanners.sh 52 | run_gitleaks './../../' 'sarif' 'info' 'true' 'true' 53 | working-directory: scripts/orchestrators 54 | 55 | - uses: actions/upload-artifact@v4 56 | if: success() || failure() 57 | with: 58 | name: gitleaks-report 59 | path: scripts/orchestrators/gitleaks-report.* 60 | 61 | - name: Upload Gitleaks SARIF report to code scanning service 62 | uses: github/codeql-action/upload-sarif@v3 63 | with: 64 | sarif_file: scripts/orchestrators/gitleaks-report.sarif 65 | 66 | - name: Run Bicep Lint 67 | env: 68 | WORKSPACE_PATH: ${{ github.workspace }} 69 | run: | 70 | ./iac.bicep.lint.sh 71 | working-directory: scripts/orchestrators 72 | 73 | - uses: azure/login@v2 74 | name: Run Azure Login 75 | with: 76 | client-id: ${{ secrets.AZURE_CLIENT_ID }} 77 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 78 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 79 | 80 | - name: Run Bicep Validate 81 | env: 82 | ENVIRONMENT_NAME: ${{ inputs.environmentName }} 83 | ENVIRONMENT_DIRECTORY: ${{ inputs.environmentDirectory }} 84 | LOCATION_NAME: ${{ inputs.locationName }} 85 | WORKSPACE_PATH: ${{ github.workspace }} 86 | RUN_ID: ${{ github.run_id }} 87 | run: | 88 | ./iac.bicep.validate.sh 89 | working-directory: scripts/orchestrators 90 | -------------------------------------------------------------------------------- /.github/workflows/template.queryevents.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Template-Query-Event" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_call: 6 | outputs: 7 | events: 8 | description: "Json array with events" 9 | value: ${{ jobs.QueryEvent.outputs.events}} 10 | inputs: 11 | pipelineName: 12 | type: string 13 | required: true 14 | eventName: 15 | type: string 16 | required: true 17 | eventGroupId: 18 | type: string 19 | required: true 20 | githubEnvironment: 21 | type: string 22 | required: true 23 | 24 | permissions: 25 | id-token: write 26 | contents: read 27 | 28 | jobs: 29 | QueryEvent: 30 | name: Query Events 31 | runs-on: ubuntu-latest 32 | environment: ${{ inputs.githubEnvironment }} 33 | outputs: 34 | events: ${{ steps.QueryEvents.outputs.events }} 35 | steps: 36 | - uses: actions/checkout@v4 37 | 38 | - uses: azure/login@v2 39 | name: Run Azure Login 40 | with: 41 | client-id: ${{ secrets.AZURE_CLIENT_ID }} 42 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 43 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 44 | 45 | - name: Query Events 46 | id: QueryEvents 47 | env: 48 | WORKSPACE_PATH: ${{ github.workspace }} 49 | run: | 50 | export EVENTS_STORAGE_ACCOUNT=${{ vars.EVENTS_STORAGE_ACCOUNT }} 51 | export EVENTS_TABLE_NAME=${{ vars.EVENTS_TABLE_NAME }} 52 | 53 | source ./events.sh 54 | 55 | events=$(query_events "${{ inputs.pipelineName }}" "${{ inputs.eventName }}" "${{ inputs.eventGroupId }}") 56 | echo "events=$events" >> $GITHUB_OUTPUT 57 | 58 | working-directory: scripts/orchestrators 59 | -------------------------------------------------------------------------------- /.github/workflows/template.storeevent.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Template-Store-Event" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_call: 6 | inputs: 7 | pipelineName: 8 | type: string 9 | required: true 10 | eventName: 11 | type: string 12 | required: true 13 | eventGroupId: 14 | type: string 15 | required: true 16 | data: 17 | type: string 18 | required: true 19 | comment: 20 | type: string 21 | required: false 22 | githubEnvironment: 23 | type: string 24 | required: true 25 | 26 | permissions: 27 | id-token: write 28 | contents: read 29 | pull-requests: write 30 | 31 | jobs: 32 | StoreEvent: 33 | name: Store Event 34 | runs-on: ubuntu-latest 35 | environment: ${{ inputs.githubEnvironment }} 36 | 37 | steps: 38 | - uses: actions/checkout@v4 39 | 40 | - uses: azure/login@v2 41 | name: Run Azure Login 42 | with: 43 | client-id: ${{ secrets.AZURE_CLIENT_ID }} 44 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 45 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 46 | 47 | - name: Store Event 48 | env: 49 | WORKSPACE_PATH: ${{ github.workspace }} 50 | run: | 51 | export EVENTS_STORAGE_ACCOUNT=${{ vars.EVENTS_STORAGE_ACCOUNT }} 52 | export EVENTS_TABLE_NAME=${{ vars.EVENTS_TABLE_NAME }} 53 | 54 | source ./events.sh 55 | 56 | store_event "${{ inputs.pipelineName }}" "${{ inputs.eventName }}" "${{ inputs.eventGroupId }}" "${{ inputs.data }}" 57 | 58 | working-directory: scripts/orchestrators 59 | 60 | - name: Comment PR 61 | if: ${{ inputs.comment != '' }} 62 | uses: actions/github-script@v7 63 | with: 64 | github-token: ${{ secrets.GITHUB_TOKEN }} 65 | script: | 66 | github.rest.issues.createComment({ 67 | issue_number: context.issue.number, 68 | owner: context.repo.owner, 69 | repo: context.repo.repo, 70 | body: `${{ inputs.comment }}` 71 | }); 72 | -------------------------------------------------------------------------------- /.github/workflows/template.terraform.cleanup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Template-Terraform-Cleanup" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_call: 6 | inputs: 7 | pullRequestNumber: 8 | type: string 9 | required: true 10 | terraformVersion: 11 | type: string 12 | required: true 13 | githubEnvironment: 14 | type: string 15 | required: true 16 | 17 | permissions: 18 | id-token: write 19 | contents: read 20 | 21 | jobs: 22 | GetEnvironments: 23 | name: GetEnvironments 24 | uses: ./.github/workflows/template.queryevents.yml 25 | with: 26 | pipelineName: PR 27 | eventName: NewEnvironment 28 | eventGroupId: PR-${{ inputs.pullRequestNumber }}-${{ github.repository_id }} 29 | githubEnvironment: ${{ inputs.githubEnvironment }} 30 | 31 | Destroy: 32 | name: Destroy 33 | needs: [GetEnvironments] 34 | runs-on: ubuntu-latest 35 | environment: ${{ inputs.githubEnvironment }} 36 | steps: 37 | - uses: actions/checkout@v4 38 | with: 39 | fetch-depth: 0 40 | 41 | - name: Setup Toolset 42 | run: | 43 | ./setup-azcli.sh 44 | ./setup-terraform.sh ${{ inputs.terraformVersion }} 45 | working-directory: scripts/orchestrators 46 | shell: bash 47 | 48 | - uses: azure/login@v2 49 | name: Run Azure Login 50 | with: 51 | client-id: ${{ secrets.AZURE_CLIENT_ID }} 52 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 53 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 54 | 55 | - name: Run TF Destroy 56 | env: 57 | WORKSPACE_PATH: ${{ github.workspace }} 58 | run: | 59 | export STATE_RG=${{ vars.STATE_RG }} 60 | export STATE_STORAGE_ACCOUNT=${{ vars.STATE_STORAGE_ACCOUNT }} 61 | export STATE_CONTAINER=${{ vars.STATE_CONTAINER }} 62 | 63 | # NEW_ENVIRONMENT_EVENTS_JSON is an array of events. 64 | # Each event has a Name, SHA and Location properties. 65 | # For each event, we want to checkout the .SHA commit, get the .Name environment name 66 | # and the config directory of the environment, and pass it to the destroy script. 67 | 68 | for event in $(echo "${NEW_ENVIRONMENT_EVENTS_JSON}" | jq -c '.[]'); do 69 | sha=$(echo "${event}" | jq -r '.SHA') 70 | name=$(echo "${event}" | jq -r '.Name') 71 | directory=$(echo "${event}" | jq -r '.Directory') 72 | 73 | git checkout "${sha}" 74 | echo "Destroying environment: $name" 75 | export ENVIRONMENT_NAME=$name 76 | export ENVIRONMENT_DIRECTORY=$directory 77 | 78 | ./iac.tf.destroy.sh 79 | done 80 | working-directory: scripts/orchestrators 81 | shell: bash 82 | -------------------------------------------------------------------------------- /.github/workflows/template.terraform.destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Template-TF-Destroy" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_call: 6 | inputs: 7 | environmentName: 8 | type: string 9 | required: true 10 | environmentDirectory: 11 | type: string 12 | required: true 13 | terraformVersion: 14 | type: string 15 | required: true 16 | branchName: 17 | type: string 18 | required: true 19 | githubEnvironment: 20 | type: string 21 | required: true 22 | 23 | permissions: 24 | id-token: write 25 | contents: read 26 | 27 | jobs: 28 | Destroy: 29 | name: "Destroy" 30 | runs-on: ubuntu-latest 31 | environment: ${{ inputs.githubEnvironment }} 32 | steps: 33 | - uses: actions/checkout@v4 34 | with: 35 | ref: ${{ inputs.branchName }} 36 | 37 | - name: Setup Toolset 38 | run: | 39 | ./setup-azcli.sh 40 | ./setup-terraform.sh ${{ inputs.terraformVersion }} 41 | working-directory: scripts/orchestrators 42 | shell: bash 43 | 44 | - uses: azure/login@v2 45 | name: Run Azure Login 46 | with: 47 | client-id: ${{ secrets.AZURE_CLIENT_ID }} 48 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 49 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 50 | 51 | - name: Run TF Destroy 52 | env: 53 | ENVIRONMENT_NAME: ${{ inputs.environmentName }} 54 | WORKSPACE_PATH: ${{ github.workspace }} 55 | ENVIRONMENT_DIRECTORY: ${{ inputs.environmentDirectory }} 56 | run: | 57 | export STATE_RG=${{ vars.STATE_RG }} 58 | export STATE_STORAGE_ACCOUNT=${{ vars.STATE_STORAGE_ACCOUNT }} 59 | export STATE_CONTAINER=${{ vars.STATE_CONTAINER }} 60 | ./iac.tf.destroy.sh 61 | working-directory: scripts/orchestrators 62 | shell: bash 63 | -------------------------------------------------------------------------------- /.github/workflows/template.terraform.previewdeploy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Template-TF-PreviewDeploy" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_call: 6 | inputs: 7 | environmentName: 8 | type: string 9 | required: true 10 | environmentDirectory: 11 | type: string 12 | required: true 13 | terraformVersion: 14 | type: string 15 | required: true 16 | branchName: 17 | type: string 18 | required: true 19 | githubEnvironment: 20 | type: string 21 | required: true 22 | 23 | permissions: 24 | id-token: write 25 | contents: read 26 | 27 | jobs: 28 | PreviewDeploy: 29 | name: "Preview and Deploy" 30 | runs-on: ubuntu-latest 31 | environment: ${{ inputs.githubEnvironment }} 32 | steps: 33 | - uses: actions/checkout@v4 34 | with: 35 | ref: ${{ inputs.branchName }} 36 | 37 | - name: Setup Toolset 38 | run: | 39 | ./setup-azcli.sh 40 | ./setup-terraform.sh ${{ inputs.terraformVersion }} 41 | working-directory: scripts/orchestrators 42 | shell: bash 43 | 44 | - uses: azure/login@v2 45 | name: Run Azure Login 46 | with: 47 | client-id: ${{ secrets.AZURE_CLIENT_ID }} 48 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 49 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 50 | 51 | - name: Run TF plan- Apply 52 | env: 53 | ENVIRONMENT_NAME: ${{ inputs.environmentName }} 54 | ENVIRONMENT_DIRECTORY: ${{ inputs.environmentDirectory }} 55 | WORKSPACE_PATH: ${{ github.workspace }} 56 | run: | 57 | export STATE_RG=${{ vars.STATE_RG }} 58 | export STATE_STORAGE_ACCOUNT=${{ vars.STATE_STORAGE_ACCOUNT }} 59 | export STATE_CONTAINER=${{ vars.STATE_CONTAINER }} 60 | export TF_VAR_env=${{ inputs.environmentName }} 61 | ./iac.tf.previewdeploy.sh 62 | 63 | working-directory: scripts/orchestrators 64 | shell: bash 65 | -------------------------------------------------------------------------------- /.github/workflows/template.terraform.report.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Template-TF-Report" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_call: 6 | inputs: 7 | environmentName: 8 | type: string 9 | required: true 10 | branchName: 11 | type: string 12 | required: true 13 | githubEnvironment: 14 | type: string 15 | required: true 16 | 17 | permissions: 18 | id-token: write 19 | contents: read 20 | 21 | jobs: 22 | Report: 23 | name: "Report" 24 | runs-on: ubuntu-latest 25 | environment: ${{ inputs.githubEnvironment }} 26 | steps: 27 | - uses: actions/checkout@v4 28 | with: 29 | ref: ${{ inputs.branchName }} 30 | 31 | - name: Setup Toolset 32 | run: | 33 | ./setup-azcli.sh 34 | 35 | working-directory: scripts/orchestrators 36 | shell: bash 37 | 38 | - uses: azure/login@v2 39 | name: Run Azure Login 40 | with: 41 | client-id: ${{ secrets.AZURE_CLIENT_ID }} 42 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 43 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 44 | 45 | - name: Backup Remote State 46 | env: 47 | ENVIRONMENT_NAME: ${{ inputs.environmentName }} 48 | COMMIT_ID: ${{ github.sha }} 49 | RUN_ID: ${{ github.run_id }} 50 | run: | 51 | export STATE_RG=${{ vars.STATE_RG }} 52 | export STATE_STORAGE_ACCOUNT=${{ vars.STATE_STORAGE_ACCOUNT }} 53 | export STATE_STORAGE_ACCOUNT_BACKUP=${{ vars.STATE_STORAGE_ACCOUNT_BACKUP }} 54 | export STATE_CONTAINER=${{ vars.STATE_CONTAINER }} 55 | 56 | ./iac.tf.statebackup.sh 57 | working-directory: scripts/orchestrators 58 | shell: bash 59 | -------------------------------------------------------------------------------- /.github/workflows/template.terraform.test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Template-TF-Test" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_call: 6 | inputs: 7 | environmentName: 8 | type: string 9 | required: true 10 | goVersion: 11 | type: string 12 | required: true 13 | terraformVersion: 14 | type: string 15 | required: true 16 | branchName: 17 | type: string 18 | required: true 19 | githubEnvironment: 20 | type: string 21 | required: true 22 | 23 | permissions: 24 | id-token: write 25 | contents: read 26 | issues: read 27 | checks: write 28 | pull-requests: write 29 | 30 | jobs: 31 | Test: 32 | name: "E2E Test" 33 | runs-on: ubuntu-latest 34 | environment: ${{ inputs.githubEnvironment }} 35 | steps: 36 | - uses: actions/checkout@v4 37 | with: 38 | ref: ${{ inputs.branchName }} 39 | 40 | - name: Setup Toolset 41 | run: | 42 | ./setup-azcli.sh 43 | ./setup-terraform.sh ${{ inputs.terraformVersion }} 44 | working-directory: scripts/orchestrators 45 | shell: bash 46 | 47 | - uses: azure/login@v2 48 | name: Run Azure Login 49 | with: 50 | client-id: ${{ secrets.AZURE_CLIENT_ID }} 51 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 52 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 53 | 54 | - uses: actions/setup-go@v5 55 | with: 56 | go-version: ${{ inputs.goVersion }} 57 | - name: Run E2E Tests 58 | env: 59 | WORKSPACE_PATH: ${{ github.workspace }} 60 | ENVIRONMENT_NAME: ${{ inputs.environmentName }} 61 | TEST_TAG: "" 62 | run: | 63 | export STATE_RG=${{ vars.STATE_RG }} 64 | export STATE_STORAGE_ACCOUNT=${{ vars.STATE_STORAGE_ACCOUNT }} 65 | export STATE_CONTAINER=${{ vars.STATE_CONTAINER }} 66 | export TF_VAR_env=${{ inputs.environmentName }} 67 | ./iac.tf.test.sh 68 | 69 | working-directory: scripts/orchestrators 70 | shell: bash 71 | 72 | - uses: actions/upload-artifact@v4 73 | if: success() || failure() 74 | with: 75 | name: E2E Test results 76 | path: ./IAC/Terraform/test/terraform/*.xml 77 | 78 | - name: Publish E2E Test Results 79 | uses: EnricoMi/publish-unit-test-result-action@v2 80 | if: always() 81 | with: 82 | files: "./IAC/Terraform/test/terraform/*.xml" 83 | check_name: "E2E Tests Results" 84 | action_fail: true 85 | -------------------------------------------------------------------------------- /.github/workflows/template.terraform.validate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Template-TF-Validate" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_call: 6 | inputs: 7 | environmentName: 8 | type: string 9 | required: true 10 | environmentDirectory: 11 | type: string 12 | required: true 13 | goVersion: 14 | type: string 15 | required: true 16 | terraformVersion: 17 | type: string 18 | required: true 19 | runLayerTest: 20 | type: string 21 | required: true 22 | branchName: 23 | type: string 24 | required: true 25 | githubEnvironment: 26 | type: string 27 | required: true 28 | 29 | permissions: 30 | id-token: write 31 | actions: write 32 | contents: write 33 | security-events: write 34 | pull-requests: write 35 | checks: write 36 | 37 | jobs: 38 | Validate: 39 | name: "Validate" 40 | runs-on: ubuntu-latest 41 | environment: ${{ inputs.githubEnvironment }} 42 | steps: 43 | - uses: actions/checkout@v4 44 | with: 45 | ref: ${{ inputs.branchName }} 46 | 47 | - name: Setup Toolset 48 | run: | 49 | ./setup-azcli.sh 50 | ./setup-gitleaks.sh 51 | ./setup-terraform.sh ${{ inputs.terraformVersion }} 52 | ./setup-tflint.sh 53 | source ~/.bashrc 54 | working-directory: scripts/orchestrators 55 | 56 | - name: Run Gitleaks 57 | run: | 58 | source ./scanners.sh 59 | run_gitleaks './../../' 'sarif' 'info' 'true' 'true' 60 | working-directory: scripts/orchestrators 61 | 62 | - uses: actions/upload-artifact@v4 63 | if: success() || failure() 64 | with: 65 | name: gitleaks-report 66 | path: scripts/orchestrators/gitleaks-report.* 67 | 68 | - name: Upload Gitleaks SARIF report to code scanning service 69 | uses: github/codeql-action/upload-sarif@v3 70 | with: 71 | sarif_file: scripts/orchestrators/gitleaks-report.sarif 72 | 73 | - name: Run TF Lint 74 | env: 75 | WORKSPACE_PATH: ${{ github.workspace }} 76 | run: | 77 | ./iac.tf.lint.sh 78 | working-directory: scripts/orchestrators 79 | shell: bash 80 | 81 | - uses: azure/login@v2 82 | name: Run Azure Login 83 | with: 84 | client-id: ${{ secrets.AZURE_CLIENT_ID }} 85 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} 86 | subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 87 | 88 | - name: Run TF Validate 89 | env: 90 | WORKSPACE_PATH: ${{ github.workspace }} 91 | ENVIRONMENT_NAME: ${{ inputs.environmentName }} 92 | run: | 93 | export STATE_RG=${{ vars.STATE_RG }} 94 | export STATE_STORAGE_ACCOUNT=${{ vars.STATE_STORAGE_ACCOUNT }} 95 | export STATE_CONTAINER=${{ vars.STATE_CONTAINER }} 96 | 97 | ./iac.tf.validate.sh 98 | working-directory: scripts/orchestrators 99 | shell: bash 100 | 101 | - uses: actions/setup-go@v5 102 | with: 103 | go-version: ${{ inputs.goVersion }} 104 | - name: Run Layer Tests 105 | if: ${{ inputs.runLayerTest == 'true' }} 106 | env: 107 | WORKSPACE_PATH: ${{ github.workspace }} 108 | ENVIRONMENT_NAME: ${{ inputs.environmentName }} 109 | ENVIRONMENT_DIRECTORY: ${{ inputs.environmentDirectory }} 110 | TEST_TAG: module_tests 111 | run: | 112 | export STATE_RG=${{ vars.STATE_RG }} 113 | export STATE_STORAGE_ACCOUNT=${{ vars.STATE_STORAGE_ACCOUNT }} 114 | export STATE_CONTAINER=${{ vars.STATE_CONTAINER }} 115 | 116 | ./iac.tf.test.sh 117 | 118 | working-directory: scripts/orchestrators 119 | shell: bash 120 | 121 | - uses: actions/upload-artifact@v4 122 | if: ${{ always() && inputs.runLayerTest == 'true' }} 123 | with: 124 | name: Modules test results 125 | path: ./IAC/Terraform/test/terraform/*.xml 126 | 127 | - name: Publish Module Test Results 128 | uses: EnricoMi/publish-unit-test-result-action@v2 129 | if: ${{ inputs.runLayerTest == 'true' }} 130 | with: 131 | files: "./IAC/Terraform/test/terraform/*.xml" 132 | check_name: "Modules tests Results" 133 | action_fail: true 134 | -------------------------------------------------------------------------------- /.github/workflows/workflow.ci.bicep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "CI Bicep" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | # push: 6 | # branches: [main] 7 | # pull_request: 8 | # branches: [main] 9 | 10 | #=============================================================# 11 | # The following Variables should be set on the workflow level # 12 | #============================================================ # 13 | workflow_dispatch: 14 | inputs: 15 | environmentName: 16 | description: "Name of the Environment" 17 | required: true 18 | default: "dev" 19 | locationName: 20 | description: "Name of the deployment Location" 21 | required: true 22 | default: "westus" 23 | 24 | permissions: 25 | id-token: write 26 | contents: read 27 | actions: read 28 | security-events: write 29 | issues: read 30 | checks: write 31 | pull-requests: write 32 | 33 | jobs: 34 | Validate: 35 | uses: ./.github/workflows/template.bicep.validate.yml 36 | with: 37 | environmentName: ${{ github.event.inputs.environmentName }} 38 | environmentDirectory: ${{ github.event.inputs.environmentName }} 39 | locationName: ${{ github.event.inputs.locationName }} 40 | branchName: ${{ github.ref_name }} 41 | githubEnvironment: symphony 42 | secrets: inherit 43 | 44 | PreviewDeploy: 45 | needs: Validate 46 | uses: ./.github/workflows/template.bicep.previewdeploy.yml 47 | with: 48 | environmentName: ${{ github.event.inputs.environmentName }} 49 | environmentDirectory: ${{ github.event.inputs.environmentName }} 50 | locationName: ${{ github.event.inputs.locationName }} 51 | branchName: ${{ github.ref_name }} 52 | githubEnvironment: symphony 53 | secrets: inherit 54 | Test: 55 | needs: PreviewDeploy 56 | uses: ./.github/workflows/template.bicep.test.yml 57 | with: 58 | environmentName: ${{ github.event.inputs.environmentName }} 59 | locationName: ${{ github.event.inputs.locationName }} 60 | branchName: ${{ github.ref_name }} 61 | githubEnvironment: symphony 62 | secrets: inherit 63 | -------------------------------------------------------------------------------- /.github/workflows/workflow.ci.terraform.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "CI Terraform" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | # push: 6 | # branches: [main] 7 | # pull_request: 8 | # branches: [main] 9 | 10 | #=============================================================# 11 | # The following Variables should be set on the workflow level # 12 | #=============================================================# 13 | workflow_dispatch: 14 | inputs: 15 | environmentName: 16 | description: "Name of the Environment" 17 | required: true 18 | default: "dev" 19 | goVersion: 20 | description: "Go Lang version" 21 | required: true 22 | default: "1.18.1" 23 | terraformVersion: 24 | description: "Terraform version" 25 | required: true 26 | default: "1.11.0" 27 | runLayerTest: 28 | description: "Run Layers Tests" 29 | type: boolean 30 | required: true 31 | default: false 32 | backupStateFiles: 33 | description: "Run Backup State Files" 34 | type: boolean 35 | required: true 36 | default: true 37 | 38 | permissions: 39 | id-token: write 40 | actions: write 41 | contents: write 42 | security-events: write 43 | pull-requests: write 44 | checks: write 45 | issues: read 46 | 47 | jobs: 48 | Validate: 49 | uses: ./.github/workflows/template.terraform.validate.yml 50 | with: 51 | environmentName: ${{ github.event.inputs.environmentName }} 52 | environmentDirectory: ${{ github.event.inputs.environmentName }} 53 | terraformVersion: ${{ github.event.inputs.terraformVersion }} 54 | goVersion: ${{ github.event.inputs.goVersion }} 55 | runLayerTest: ${{ github.event.inputs.runLayerTest }} 56 | branchName: ${{ github.ref_name }} 57 | githubEnvironment: symphony 58 | secrets: inherit 59 | 60 | PreviewDeploy: 61 | needs: Validate 62 | uses: ./.github/workflows/template.terraform.previewdeploy.yml 63 | with: 64 | environmentName: ${{ github.event.inputs.environmentName }} 65 | environmentDirectory: ${{ github.event.inputs.environmentName }} 66 | terraformVersion: ${{ github.event.inputs.terraformVersion }} 67 | branchName: ${{ github.ref_name }} 68 | githubEnvironment: symphony 69 | secrets: inherit 70 | 71 | Test: 72 | uses: ./.github/workflows/template.terraform.test.yml 73 | needs: PreviewDeploy 74 | with: 75 | environmentName: ${{ github.event.inputs.environmentName }} 76 | terraformVersion: ${{ github.event.inputs.terraformVersion }} 77 | goVersion: ${{ github.event.inputs.goVersion }} 78 | branchName: ${{ github.ref_name }} 79 | githubEnvironment: symphony 80 | secrets: inherit 81 | 82 | Report: 83 | needs: PreviewDeploy 84 | if: ${{ github.event.inputs.backupStateFiles == 'true' }} 85 | uses: ./.github/workflows/template.terraform.report.yml 86 | with: 87 | environmentName: ${{ github.event.inputs.environmentName }} 88 | branchName: ${{ github.ref_name }} 89 | githubEnvironment: symphony 90 | secrets: inherit 91 | -------------------------------------------------------------------------------- /.github/workflows/workflow.cleanup.bicep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Cleanup Bicep" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_dispatch: 6 | inputs: 7 | pullRequestNumber: 8 | description: "Pull Request Number" 9 | required: true 10 | 11 | permissions: 12 | id-token: write 13 | contents: read 14 | 15 | jobs: 16 | Destroy: 17 | uses: ./.github/workflows/template.bicep.cleanup.yml 18 | with: 19 | pullRequestnumber: ${{ github.event.inputs.pullRequestNumber }} 20 | githubEnvironment: symphony 21 | secrets: inherit 22 | -------------------------------------------------------------------------------- /.github/workflows/workflow.cleanup.terraform.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Cleanup Terraform" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_dispatch: 6 | inputs: 7 | pullRequestNumber: 8 | description: "Pull Request Number" 9 | required: true 10 | terraformVersion: 11 | type: string 12 | required: true 13 | default: "1.11.0" 14 | 15 | permissions: 16 | id-token: write 17 | contents: read 18 | 19 | jobs: 20 | Destroy: 21 | uses: ./.github/workflows/template.terraform.cleanup.yml 22 | with: 23 | pullRequestnumber: ${{ github.event.inputs.pullRequestNumber }} 24 | terraformVersion: ${{ github.event.inputs.terraformVersion }} 25 | githubEnvironment: symphony 26 | secrets: inherit 27 | -------------------------------------------------------------------------------- /.github/workflows/workflow.destroy.bicep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Destroy Bicep" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | # push: 6 | # branches: [main] 7 | # pull_request: 8 | # branches: [main] 9 | 10 | #=============================================================# 11 | # The following Variables should be set on the workflow level # 12 | #============================================================ # 13 | workflow_dispatch: 14 | inputs: 15 | environmentName: 16 | description: "Name of the Environment" 17 | required: true 18 | default: "dev" 19 | locationName: 20 | description: "Name of the deployment Location" 21 | required: true 22 | default: "westus" 23 | 24 | permissions: 25 | id-token: write 26 | contents: read 27 | 28 | jobs: 29 | Destroy: 30 | uses: ./.github/workflows/template.bicep.destroy.yml 31 | with: 32 | environmentName: ${{ github.event.inputs.environmentName }} 33 | locationName: ${{ github.event.inputs.locationName }} 34 | branchName: ${{ github.ref_name }} 35 | githubEnvironment: symphony 36 | secrets: inherit 37 | -------------------------------------------------------------------------------- /.github/workflows/workflow.destroy.terraform.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Destroy Terraform" 3 | 4 | on: # yamllint disable-line rule:truthy 5 | #=============================================================# 6 | # The following Variables should be set on the workflow level # 7 | #=============================================================# 8 | workflow_dispatch: 9 | inputs: 10 | environmentName: 11 | description: "Name of the Environment" 12 | required: true 13 | default: "dev" 14 | terraformVersion: 15 | description: "Terraform version" 16 | required: true 17 | default: "1.11.0" 18 | 19 | permissions: 20 | id-token: write 21 | contents: read 22 | 23 | jobs: 24 | Destroy: 25 | uses: ./.github/workflows/template.terraform.destroy.yml 26 | with: 27 | environmentName: ${{ github.event.inputs.environmentName }} 28 | environmentDirectory: ${{ github.event.inputs.environmentName }} 29 | terraformVersion: ${{ github.event.inputs.terraformVersion }} 30 | branchName: ${{ github.ref_name }} 31 | githubEnvironment: symphony 32 | secrets: inherit 33 | -------------------------------------------------------------------------------- /.github/workflows/workflow.pr.bicep.cleanup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: PR Cleanup Bicep 3 | 4 | on: # yamllint disable-line rule:truthy 5 | pull_request_target: 6 | types: 7 | - closed 8 | branches: 9 | - main 10 | 11 | concurrency: 12 | group: pr-${{ github.event.pull_request.number }} 13 | 14 | 15 | permissions: 16 | id-token: write 17 | contents: read 18 | 19 | jobs: 20 | Cleanup: 21 | uses: ./.github/workflows/template.bicep.cleanup.yml 22 | with: 23 | pullRequestNumber: ${{ github.event.pull_request.number }} 24 | githubEnvironment: symphony 25 | secrets: inherit 26 | -------------------------------------------------------------------------------- /.github/workflows/workflow.pr.terraform.cleanup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: PR Cleanup Terraform 3 | 4 | on: # yamllint disable-line rule:truthy 5 | pull_request_target: 6 | types: 7 | - closed 8 | branches: 9 | - main 10 | 11 | concurrency: 12 | group: pr-${{ github.event.pull_request.number }} 13 | 14 | env: 15 | TERRAFORM_VERSION: "1.11.0" 16 | 17 | permissions: 18 | id-token: write 19 | contents: read 20 | 21 | jobs: 22 | Setup: 23 | runs-on: ubuntu-latest 24 | outputs: 25 | terraformVersion: ${{ steps.setValues.outputs.terraformVersion }} 26 | steps: 27 | - name: Set Values 28 | id: setValues 29 | run: | 30 | # shellcheck source=/dev/null 31 | echo "terraformVersion=$TERRAFORM_VERSION" >> $GITHUB_OUTPUT 32 | 33 | Cleanup: 34 | needs: [Setup] 35 | uses: ./.github/workflows/template.terraform.cleanup.yml 36 | with: 37 | pullRequestNumber: ${{ github.event.pull_request.number }} 38 | terraformVersion: ${{ needs.Setup.outputs.terraformVersion }} 39 | githubEnvironment: symphony 40 | secrets: inherit 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | untitled*.yml 2 | arm-ttk/ 3 | .DS_Store 4 | .secrets 5 | .env 6 | .actrc 7 | .symphony 8 | IAC/**/*lint_res.xml 9 | megalinter-reports/ 10 | .lycheecache 11 | -------------------------------------------------------------------------------- /.lycheeignore: -------------------------------------------------------------------------------- 1 | # These links are ignored by lychee link checker: https://github.com/lycheeverse/lychee 2 | # The file allows you to list multiple regular expressions for exclusion (one pattern per line). 3 | # The `.lycheeignore` file is only used for excluding URLs, not paths. Use the `exclude_path` key in the `lychee.toml` file. ref: https://github.com/lycheeverse/lycheeverse.github.io/blob/master/recipes/excluding-paths.md 4 | 5 | https://megalinter.io/configuration 6 | https://vsrm.dev.azure.com/__ADO_ORG_NAME__ 7 | https://opensource.microsoft.com/codeofconduct/faq 8 | -------------------------------------------------------------------------------- /.mega-linter.yml: -------------------------------------------------------------------------------- 1 | # Configuration file for MegaLinter 2 | # See all available variables at https://megalinter.io/latest/config-file/ and in linters documentation 3 | --- 4 | PARALLEL: true 5 | DEFAULT_BRANCH: main 6 | SHOW_ELAPSED_TIME: true 7 | CLEAR_REPORT_FOLDER: true 8 | OUTPUT_DETAIL: detailed 9 | GITHUB_COMMENT_REPORTER: true 10 | GITHUB_STATUS_REPORTER: false 11 | JSON_REPORTER: true 12 | JSON_REPORTER_OUTPUT_DETAIL: simple 13 | SARIF_REPORTER: true 14 | FILEIO_REPORTER: false 15 | FORMATTERS_DISABLE_ERRORS: false 16 | JAVASCRIPT_DEFAULT_STYLE: prettier 17 | ENABLE_LINTERS: 18 | - MARKDOWN_MARKDOWNLINT 19 | - MARKDOWN_MARKDOWN_TABLE_FORMATTER 20 | - SPELL_LYCHEE 21 | - REPOSITORY_GITLEAKS 22 | - ENV_DOTENV_LINTER 23 | - YAML_YAMLLINT 24 | - JSON_JSONLINT 25 | - ACTION_ACTIONLINT 26 | - EDITORCONFIG_EDITORCONFIG_CHECKER 27 | EXCLUDED_DIRECTORIES: 28 | - '.git' 29 | - 'node_modules' 30 | - '.dev' 31 | FILTER_REGEX_EXCLUDE: (.git/|node_modules/|.dev/|.*-megalinter_*\.txt|.*\.drawio|.*\.mmd|.*\.svg|.*\.SVG) 32 | MARKDOWN_MARKDOWNLINT_ARGUMENTS: --disable MD025 33 | MARKDOWN_MARKDOWNLINT_CONFIG_FILE: .markdownlint.yml 34 | MARKDOWN_MARKDOWN_LINK_CHECK_ARGUMENTS: --quiet 35 | REPOSITORY_GITLEAKS_CONFIG_FILE: .gitleaks.toml 36 | REPOSITORY_GITLEAKS_ARGUMENTS: --redact 37 | ENV_DOTENV_LINTER_FILE_EXTENSIONS: ['.env', '.env.*'] 38 | YAML_YAMLLINT_CONFIG_FILE: .yamllint.yml 39 | # COPYPASTE_JSCPD_CONFIG_FILE: .jscpd.json 40 | JSON_JSONLINT_CONFIG_FILE: .jsonlintrc.json 41 | # ACTION_ACTIONLINT_CONFIG_FILE: .actionlint.yml 42 | ACTION_ACTIONLINT_ARGUMENTS: -shellcheck 43 | LINTER_RULES_PATH: .github/linters 44 | SPELL_LYCHEE_CONFIG_FILE: .lychee.toml 45 | # LOG_LEVEL: DEBUG 46 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // editor 3 | "editor.formatOnSave": true, 4 | "editor.detectIndentation": true, 5 | // files 6 | "files.trimTrailingWhitespace": true, 7 | "files.insertFinalNewline": true, 8 | // yaml 9 | "yaml.format.singleQuote": false 10 | } 11 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to and actually do, grant us the rights to use your contribution. For details, visit [Microsoft Contributor License Agreement](https://cla.opensource.microsoft.com). 4 | 5 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. 6 | 7 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 8 | -------------------------------------------------------------------------------- /IAC/Bicep/bicep/01_storage/01_rg/main.bicep: -------------------------------------------------------------------------------- 1 | targetScope = 'subscription' 2 | 3 | param environment string 4 | param layerName string 5 | param location string = deployment().location 6 | param deploymentName string = '' 7 | param resourceGroupName string = '' 8 | 9 | var _resourceGroupName = empty(deploymentName) ? resourceGroupNameGenerator.outputs.name : resourceGroupName 10 | var _deploymentName = empty(deploymentName) 11 | ? uniqueString(subscription().subscriptionId, location, environment) 12 | : deploymentName 13 | var uniqueToken = substring(uniqueString(_deploymentName, layerName), 0, 6) 14 | 15 | // Resource Group 16 | 17 | module resourceGroupNameGenerator './../../modules/nameGeneratorSubscription.bicep' = { 18 | name: '${_deploymentName}-resourceGroupNameGenerator' 19 | params: { 20 | name: 'rg-storage' 21 | prefix: environment 22 | uniqueToken: uniqueToken 23 | } 24 | } 25 | 26 | module resourceGroup './../../modules/resourceGroup.bicep' = { 27 | name: '${_deploymentName}-resourceGroup' 28 | params: { 29 | name: _resourceGroupName 30 | layerName: layerName 31 | location: location 32 | environment: environment 33 | } 34 | } 35 | 36 | output resourceGroupId string = resourceGroup.outputs.id 37 | output resourceGroupName string = resourceGroup.outputs.name 38 | -------------------------------------------------------------------------------- /IAC/Bicep/bicep/01_storage/02_deployment/main.bicep: -------------------------------------------------------------------------------- 1 | param location string = resourceGroup().location 2 | param environment string 3 | param layerName string 4 | param deploymentName string = '' 5 | 6 | var _deploymentName = empty(deploymentName) 7 | ? uniqueString(subscription().subscriptionId, location, environment) 8 | : deploymentName 9 | 10 | var namePrefix = '${environment}sa' 11 | var nameSuffix = substring(uniqueString(location, subscription().id, guid(namePrefix), resourceGroup().id), 0, 6) 12 | var name = '${namePrefix}${nameSuffix}' 13 | 14 | // Storage Account 15 | 16 | module storageAccount './modules/storageAccount.bicep' = { 17 | name: '${_deploymentName}-storage' 18 | params: { 19 | name: name 20 | location: location 21 | environment: environment 22 | layerName: layerName 23 | } 24 | } 25 | 26 | output storageAccountResourceGroupName string = resourceGroup().name 27 | output storageAccountName string = storageAccount.outputs.name 28 | -------------------------------------------------------------------------------- /IAC/Bicep/bicep/01_storage/02_deployment/modules/storageAccount.bicep: -------------------------------------------------------------------------------- 1 | param name string 2 | param location string 3 | param environment string 4 | param layerName string 5 | 6 | // Storage Account 7 | 8 | // https://learn.microsoft.com/en-us/azure/templates/microsoft.storage/storageaccounts?pivots=deployment-language-bicep 9 | resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = { 10 | name: name 11 | location: location 12 | sku: { 13 | name: 'Standard_LRS' 14 | } 15 | kind: 'StorageV2' 16 | tags: { 17 | EnvironmentName: environment 18 | LayerName: layerName 19 | GeneratedBy: 'symphony' 20 | } 21 | } 22 | 23 | output id string = storageAccount.id 24 | output name string = storageAccount.name 25 | -------------------------------------------------------------------------------- /IAC/Bicep/bicep/02_config/01_rg/main.bicep: -------------------------------------------------------------------------------- 1 | targetScope = 'subscription' 2 | 3 | param environment string 4 | param layerName string 5 | param location string = deployment().location 6 | param deploymentName string = '' 7 | param resourceGroupName string = '' 8 | 9 | var _resourceGroupName = empty(deploymentName) ? resourceGroupNameGenerator.outputs.name : resourceGroupName 10 | var _deploymentName = empty(deploymentName) 11 | ? uniqueString(subscription().subscriptionId, location, environment) 12 | : deploymentName 13 | var uniqueToken = substring(uniqueString(_deploymentName, layerName), 0, 6) 14 | 15 | // Resource Group 16 | 17 | module resourceGroupNameGenerator './../../modules/nameGeneratorSubscription.bicep' = { 18 | name: '${_deploymentName}-resourceGroupNameGenerator' 19 | params: { 20 | name: 'rg-config' 21 | prefix: environment 22 | uniqueToken: uniqueToken 23 | } 24 | } 25 | 26 | module resourceGroup './../../modules/resourceGroup.bicep' = { 27 | name: '${_deploymentName}-resourceGroup' 28 | params: { 29 | name: _resourceGroupName 30 | layerName: layerName 31 | location: location 32 | environment: environment 33 | } 34 | } 35 | 36 | output resourceGroupId string = resourceGroup.outputs.id 37 | output resourceGroupName string = resourceGroup.outputs.name 38 | -------------------------------------------------------------------------------- /IAC/Bicep/bicep/02_config/02_deployment/_events.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # pre_deploy method is called before the deployment 4 | pre_deploy() { 5 | # placeholder for pre deploy logic 6 | return $? 7 | } 8 | 9 | # post_deploy method is called after the deployment 10 | post_deploy() { 11 | # placeholder for post deploy logic 12 | return $? 13 | } 14 | 15 | # pre_validate method is called before the deployment validation 16 | pre_validate() { 17 | export storageAccountName="storageAccountName" 18 | return $? 19 | } 20 | 21 | # post_validate method is called after the deployment validation 22 | post_validate() { 23 | # placeholder for post deployment validate logic 24 | return $? 25 | } 26 | -------------------------------------------------------------------------------- /IAC/Bicep/bicep/02_config/02_deployment/main.bicep: -------------------------------------------------------------------------------- 1 | param location string = resourceGroup().location 2 | param environment string 3 | param layerName string 4 | param storageAccountName string 5 | param deploymentName string = '' 6 | 7 | var _deploymentName = empty(deploymentName) 8 | ? uniqueString(subscription().subscriptionId, location, environment) 9 | : deploymentName 10 | 11 | var uniqueToken = substring(uniqueString(location, subscription().id, guid('appconfig'), resourceGroup().id), 0, 6) 12 | 13 | // App Configuration 14 | 15 | module appConfigName './../../modules/nameGenerator.bicep' = { 16 | name: '${_deploymentName}-appConfigName' 17 | params: { 18 | name: 'config' 19 | prefix: environment 20 | uniqueToken: uniqueToken 21 | suffixLength: 6 22 | } 23 | } 24 | 25 | module appConfig './modules/appconfig.bicep' = { 26 | name: '${_deploymentName}-appConfig' 27 | params: { 28 | name: appConfigName.outputs.name 29 | location: location 30 | environment: environment 31 | layerName: layerName 32 | configKeyValues: [ 33 | { 34 | name: 'storageAccountName' 35 | value: storageAccountName 36 | } 37 | ] 38 | } 39 | } 40 | 41 | output appConfigResourceGroupName string = resourceGroup().name 42 | output appConfigAccountName string = appConfig.outputs.name 43 | output appConfigItemsLength int = length(appConfig.outputs.keys) 44 | -------------------------------------------------------------------------------- /IAC/Bicep/bicep/02_config/02_deployment/modules/appconfig.bicep: -------------------------------------------------------------------------------- 1 | param name string 2 | param location string 3 | param environment string 4 | param layerName string 5 | param configKeyValues object[] 6 | 7 | // App Configuration 8 | 9 | // https://learn.microsoft.com/en-us/azure/templates/microsoft.appconfiguration/configurationstores?pivots=deployment-language-bicep 10 | resource appConfig 'Microsoft.AppConfiguration/configurationStores@2022-05-01' = { 11 | name: name 12 | location: location 13 | sku: { 14 | name: 'Standard' 15 | } 16 | tags: { 17 | EnvironmentName: environment 18 | LayerName: layerName 19 | GeneratedBy: 'symphony' 20 | } 21 | } 22 | 23 | // https://learn.microsoft.com/en-us/azure/templates/microsoft.appconfiguration/configurationstores/keyvalues?pivots=deployment-language-bicep 24 | resource appConfigEntries 'Microsoft.AppConfiguration/configurationStores/keyValues@2022-05-01' = [ 25 | for kv in configKeyValues: { 26 | name: kv.name 27 | parent: appConfig 28 | properties: { 29 | value: kv.value 30 | contentType: 'text/plain' 31 | } 32 | } 33 | ] 34 | 35 | output id string = appConfig.id 36 | output name string = appConfig.name 37 | output keys array = [for kv in configKeyValues: kv.name] 38 | -------------------------------------------------------------------------------- /IAC/Bicep/bicep/modules/nameGenerator.bicep: -------------------------------------------------------------------------------- 1 | @maxLength(12) 2 | param name string 3 | 4 | @maxLength(1) 5 | param delimiter string = '-' 6 | 7 | @maxLength(6) 8 | param prefix string = '' 9 | 10 | @maxLength(6) 11 | param suffix string = '' 12 | 13 | @maxLength(6) 14 | param uniqueToken string = '' 15 | 16 | @maxValue(6) 17 | @minValue(0) 18 | param suffixLength int = 3 19 | 20 | var outputPrefix = empty(prefix) ? name : '${prefix}${delimiter}${name}' 21 | var outputSuffixTemp = empty(suffix) ? substring(uniqueString(uniqueToken,subscription().id,guid(outputPrefix)), 0, suffixLength) : suffix 22 | var output = empty(outputSuffixTemp) ? outputPrefix : '${outputPrefix}${delimiter}${outputSuffixTemp}' 23 | 24 | output name string = output 25 | -------------------------------------------------------------------------------- /IAC/Bicep/bicep/modules/nameGeneratorSubscription.bicep: -------------------------------------------------------------------------------- 1 | targetScope = 'subscription' 2 | 3 | @maxLength(12) 4 | param name string 5 | 6 | @maxLength(1) 7 | param delimiter string = '-' 8 | 9 | @maxLength(6) 10 | param prefix string = '' 11 | 12 | @maxLength(6) 13 | param suffix string = '' 14 | 15 | @maxLength(6) 16 | param uniqueToken string = '' 17 | 18 | @maxValue(6) 19 | @minValue(0) 20 | param suffixLength int = 3 21 | 22 | param suffixGenerated bool = true 23 | 24 | var outputPrefix = empty(prefix) ? name : '${prefix}${delimiter}${name}' 25 | var outputSuffixTemp = suffixGenerated 26 | ? substring(uniqueString(uniqueToken, subscription().id, guid(outputPrefix)), 0, suffixLength) 27 | : suffix 28 | var output = empty(outputSuffixTemp) ? outputPrefix : '${outputPrefix}${delimiter}${outputSuffixTemp}' 29 | 30 | output name string = output 31 | -------------------------------------------------------------------------------- /IAC/Bicep/bicep/modules/randomGenerator.bicep: -------------------------------------------------------------------------------- 1 | param input string = utcNow() 2 | 3 | var output = uniqueString(guid(input)) 4 | 5 | output result string = output 6 | -------------------------------------------------------------------------------- /IAC/Bicep/bicep/modules/resourceGroup.bicep: -------------------------------------------------------------------------------- 1 | targetScope = 'subscription' 2 | 3 | param name string 4 | param location string 5 | param environment string 6 | param layerName string 7 | 8 | // https://docs.microsoft.com/en-us/azure/templates/microsoft.resources/resourcegroups?tabs=bicep 9 | resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-07-01' = { 10 | name: name 11 | location: location 12 | tags: { 13 | EnvironmentName: environment 14 | LayerName: layerName 15 | GeneratedBy: 'symphony' 16 | } 17 | } 18 | 19 | output name string = resourceGroup.name 20 | output id string = resourceGroup.id 21 | -------------------------------------------------------------------------------- /IAC/Bicep/test/end_to_end/End_To_End.Tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll { 2 | Import-Module BenchPress.Azure 3 | 4 | # arrange 5 | $storageAccountResourceGroupName = $env:storageAccountResourceGroupName 6 | $storageAccountName = $env:storageAccountName 7 | $appConfigResourceGroupName = $env:appConfigResourceGroupName 8 | $appConfigAccountName = $env:appConfigAccountName 9 | $appConfigItemsLength = $env:appConfigItemsLength 10 | } 11 | 12 | Describe "End to End Tests" { 13 | Context "Test end to end" { 14 | It "should check the parameters and environment variables configured correctly" { 15 | # act and assert 16 | $storageAccountResourceGroupName | Should -Not -Be $Null 17 | $storageAccountName | Should -Not -Be $Null 18 | $appConfigResourceGroupName | Should -Not -Be $Null 19 | $appConfigAccountName | Should -Not -Be $Null 20 | $appConfigItemsLength | Should -BeGreaterThan 0 21 | } 22 | 23 | It "should check the storage account is ready" { 24 | # act and assert 25 | $storageAccountResource = Confirm-AzBPStorageAccount -ResourceGroupName $storageAccountResourceGroupName -Name $storageAccountName 26 | $storageAccountResource | Should -Be -Not $null 27 | } 28 | 29 | It "should check the storage account is configured correctly" { 30 | # act and assert 31 | $storageAccountResource = Confirm-AzBPStorageAccount -ResourceGroupName $storageAccountResourceGroupName -Name $storageAccountName 32 | 33 | $storageAccountResource.ResourceDetails.ProvisioningState | Should -Be "Succeeded" 34 | $storageAccountResource.ResourceDetails.Kind | Should -Be "StorageV2" 35 | $storageAccountResource.ResourceDetails.Sku.Name | Should -Be "Standard_LRS" 36 | $storageAccountResource.ResourceDetails.EnableHttpsTrafficOnly | Should -Be $true 37 | } 38 | 39 | It "should check the app config is ready" { 40 | # act and assert 41 | $appConfigResource = Get-AzBPResource -ResourceName $appConfigAccountName -ResourceGroupName $appConfigResourceGroupName 42 | 43 | $appConfigResource | Should -Be -Not $null 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /IAC/Terraform/.gitignore: -------------------------------------------------------------------------------- 1 | ### IntelliJ IDEA ### 2 | .idea 3 | *.iws 4 | *.iml 5 | *.ipr 6 | /out/ 7 | /secrets 8 | 9 | ### Terraform ### 10 | !IAC/Terraform/test/terraform/mocked_deployment.tfstate 11 | **/.terraform 12 | *.tfstate 13 | *.tfplan 14 | *.backup 15 | *.plan 16 | .terraform.lock.hcl 17 | *.lock.info 18 | 19 | ### VSCode ### 20 | **/.vscode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | ### OTHER ### 28 | /temp 29 | **/*/plugins/* 30 | .DS_Store 31 | tools/**/local-* 32 | tools/install/temp 33 | tools/install/users.csv 34 | debug.log 35 | environments/dev/arm.env 36 | environments/dev/backend.tfvars 37 | environments/**/*.compiled.env 38 | scripts/env/out 39 | scripts/env/*.compiled.env 40 | .envrc 41 | !.devcontainer/scripts/.envrc 42 | terraform/**/_override.tf 43 | -------------------------------------------------------------------------------- /IAC/Terraform/.tflint.hcl: -------------------------------------------------------------------------------- 1 | rule "terraform_module_pinned_source" { 2 | enabled = false 3 | } 4 | -------------------------------------------------------------------------------- /IAC/Terraform/terraform/01_init/main.tf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------------------------------ 2 | # DEPLOY primary and backup reources group, storage accounts, and containers for Remote state 3 | # ------------------------------------------------------------------------------------------------------ 4 | 5 | # ------------------------------------------------------------------------------------------------------ 6 | # Deploy primary resource Group 7 | # ------------------------------------------------------------------------------------------------------ 8 | resource "azurerm_resource_group" "tfstate_rg" { 9 | name = var.resource_group_name 10 | location = var.location 11 | tags = { 12 | GeneratedBy = "symphony" 13 | } 14 | } 15 | 16 | # ------------------------------------------------------------------------------------------------------ 17 | # Deploy primary Storage Account 18 | # ------------------------------------------------------------------------------------------------------ 19 | resource "azurerm_storage_account" "tfstate" { 20 | name = var.storage_account_name # globally unique 21 | resource_group_name = azurerm_resource_group.tfstate_rg.name 22 | location = azurerm_resource_group.tfstate_rg.location 23 | account_tier = var.storage_account_tier 24 | account_replication_type = var.storage_account_replication_type # LRS, GRS, RAGRS, ZRS 25 | account_kind = var.storage_account_kind 26 | 27 | identity { 28 | type = var.identity_type 29 | } 30 | 31 | tags = { 32 | env = var.env 33 | version = var.env_version 34 | } 35 | } 36 | 37 | # ------------------------------------------------------------------------------------------------------ 38 | # Deploy primary Storage Account Container 39 | # ------------------------------------------------------------------------------------------------------ 40 | resource "azurerm_storage_container" "tfstate_container" { 41 | name = var.container_name 42 | storage_account_id = azurerm_storage_account.tfstate.id 43 | container_access_type = "private" 44 | } 45 | 46 | # ------------------------------------------------------------------------------------------------------ 47 | # Deploy backup resource Group 48 | # ------------------------------------------------------------------------------------------------------ 49 | resource "azurerm_resource_group" "tfstatebak_rg" { 50 | name = var.backup_resource_group_name 51 | location = var.location 52 | tags = { 53 | GeneratedBy = "symphony" 54 | } 55 | } 56 | 57 | # ------------------------------------------------------------------------------------------------------ 58 | # Deploy backup Storage Account 59 | # ------------------------------------------------------------------------------------------------------ 60 | resource "azurerm_storage_account" "tfstatebak" { 61 | name = var.backup_storage_account_name 62 | resource_group_name = azurerm_resource_group.tfstatebak_rg.name 63 | location = azurerm_resource_group.tfstatebak_rg.location 64 | account_tier = var.storage_account_tier 65 | account_replication_type = var.storage_account_replication_type 66 | account_kind = var.storage_account_kind 67 | 68 | identity { 69 | type = var.identity_type 70 | } 71 | 72 | tags = { 73 | env = var.env 74 | version = var.env_version 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /IAC/Terraform/terraform/01_init/output.tf: -------------------------------------------------------------------------------- 1 | output "resource_group_name" { 2 | value = azurerm_resource_group.tfstate_rg.name 3 | } 4 | output "storage_account_name" { 5 | value = azurerm_storage_account.tfstate.name 6 | } 7 | output "container_name" { 8 | value = azurerm_storage_container.tfstate_container.name 9 | } 10 | output "backup_resource_group_name" { 11 | value = azurerm_resource_group.tfstatebak_rg.name 12 | } 13 | output "backup_storage_account_name" { 14 | value = azurerm_storage_account.tfstatebak.name 15 | } 16 | -------------------------------------------------------------------------------- /IAC/Terraform/terraform/01_init/provider.tf: -------------------------------------------------------------------------------- 1 | /* 2 | initialize the Azure environment. No remote storage, only performed once. 3 | */ 4 | 5 | #Set the terraform required version, and Configure the Azure Provider 6 | terraform { 7 | required_version = ">= 1.11.0, < 2.0.0" 8 | required_providers { 9 | azurerm = { 10 | source = "hashicorp/azurerm" 11 | version = "=4.1.0" 12 | } 13 | } 14 | } 15 | 16 | provider "azurerm" { 17 | features {} 18 | } 19 | -------------------------------------------------------------------------------- /IAC/Terraform/terraform/01_init/variables.tf: -------------------------------------------------------------------------------- 1 | variable "location" { 2 | description = "The supported azure location where the resource deployed" 3 | type = string 4 | } 5 | 6 | variable "env" { 7 | description = "The name of the environment to be deployed" 8 | type = string 9 | } 10 | 11 | variable "resource_group_name" { 12 | description = "the name of the AzureRm backend resource group" 13 | type = string 14 | } 15 | variable "storage_account_name" { 16 | description = "the name of the AzureRm backend storage account" 17 | type = string 18 | } 19 | 20 | variable "storage_account_tier" { 21 | description = "the tier of the AzureRm backend storage account" 22 | type = string 23 | } 24 | 25 | variable "storage_account_replication_type" { 26 | description = "the replication type of the AzureRm backend storage account e.g. LRS, ZRS,GRS" 27 | type = string 28 | } 29 | 30 | variable "storage_account_kind" { 31 | description = "the kind type of the AzureRm backend storage account e.g. BlobStorage, BlockBlobStorage, StorageV2" 32 | type = string 33 | } 34 | 35 | variable "identity_type" { 36 | description = "storage account identity type e.g. SystemAssigned" 37 | type = string 38 | } 39 | 40 | variable "container_name" { 41 | description = "the name of the AzureRm backend storage account container" 42 | type = string 43 | } 44 | 45 | variable "backup_resource_group_name" { 46 | description = "the name of the AzureRm backend backup resource group" 47 | type = string 48 | } 49 | 50 | variable "backup_storage_account_name" { 51 | description = "the name of the AzureRm backend backup storage account" 52 | type = string 53 | } 54 | 55 | variable "env_version" { 56 | description = "The version of the environment to be deployed" 57 | type = string 58 | } 59 | -------------------------------------------------------------------------------- /IAC/Terraform/terraform/02_storage/01_deployment/_events.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # pre_deploy method is called before the deployment 4 | pre_deploy() { 5 | # placeholder for pre deploy logic 6 | return $? 7 | } 8 | 9 | # post_deploy method is called after the deployment 10 | post_deploy() { 11 | # placeholder for post deploy logic 12 | return $? 13 | } 14 | 15 | # pre_validate method is called before the deployment validation 16 | pre_validate() { 17 | # placeholder for pre deployment validate logic 18 | return $? 19 | } 20 | 21 | # post_validate method is called after the deployment validation 22 | post_validate() { 23 | # placeholder for post deployment validate logic 24 | return $? 25 | } 26 | -------------------------------------------------------------------------------- /IAC/Terraform/terraform/02_storage/01_deployment/main.tf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------------------------------ 2 | # DEPLOY a reources group and a storage account - Uses Remote state 3 | # ------------------------------------------------------------------------------------------------------ 4 | 5 | # ------------------------------------------------------------------------------------------------------ 6 | # Deploy resource Group 7 | # ------------------------------------------------------------------------------------------------------ 8 | resource "azurecaf_name" "rg_name" { 9 | name = "storage" 10 | resource_type = "azurerm_resource_group" 11 | prefixes = [var.env] 12 | random_length = 3 13 | clean_input = true 14 | } 15 | resource "azurerm_resource_group" "rg" { 16 | name = azurecaf_name.rg_name.result 17 | location = var.location 18 | 19 | tags = { 20 | env = var.env, 21 | GeneratedBy = "symphony" 22 | } 23 | } 24 | 25 | # ------------------------------------------------------------------------------------------------------ 26 | # Deploy storage account 27 | # ------------------------------------------------------------------------------------------------------ 28 | resource "azurecaf_name" "storage_name" { 29 | name = "storage" 30 | resource_type = "azurerm_storage_account" 31 | prefixes = [var.env] 32 | random_length = 3 33 | clean_input = true 34 | } 35 | resource "azurerm_storage_account" "storage" { 36 | name = azurecaf_name.storage_name.result 37 | resource_group_name = azurerm_resource_group.rg.name 38 | location = azurerm_resource_group.rg.location 39 | account_tier = "Standard" 40 | account_replication_type = "LRS" 41 | account_kind = "StorageV2" 42 | 43 | tags = { env : var.env } 44 | } 45 | -------------------------------------------------------------------------------- /IAC/Terraform/terraform/02_storage/01_deployment/output.tf: -------------------------------------------------------------------------------- 1 | output "resource_group_name" { 2 | value = azurerm_resource_group.rg.name 3 | } 4 | output "storage_account_name" { 5 | value = azurerm_storage_account.storage.name 6 | } 7 | -------------------------------------------------------------------------------- /IAC/Terraform/terraform/02_storage/01_deployment/provider.tf: -------------------------------------------------------------------------------- 1 | #Set the terraform required version, and Configure the Azure Provider.Use remote storage 2 | 3 | # Configure the Azure Provider 4 | terraform { 5 | required_version = ">= 1.11.0, < 2.0.0" 6 | backend "azurerm" {} 7 | required_providers { 8 | azurerm = { 9 | source = "hashicorp/azurerm" 10 | version = "=4.1.0" 11 | } 12 | azurecaf = { 13 | source = "aztfmod/azurecaf" 14 | version = "1.2.15" 15 | } 16 | random = { 17 | source = "hashicorp/random" 18 | version = "~> 3.0" 19 | } 20 | } 21 | } 22 | 23 | provider "azurerm" { 24 | features {} 25 | } 26 | -------------------------------------------------------------------------------- /IAC/Terraform/terraform/02_storage/01_deployment/variables.tf: -------------------------------------------------------------------------------- 1 | variable "location" { 2 | description = "The supported azure location where the resource deployed" 3 | type = string 4 | } 5 | 6 | variable "env" { 7 | description = "The name of the environment to be deployed" 8 | type = string 9 | } 10 | -------------------------------------------------------------------------------- /IAC/Terraform/terraform/03_config/01_deployment/_events.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # pre_deploy method is called before the deployment 4 | pre_deploy() { 5 | # placeholder for pre deploy logic 6 | return $? 7 | } 8 | 9 | # post_deploy method is called after the deployment 10 | post_deploy() { 11 | # placeholder for post deploy logic 12 | return $? 13 | } 14 | 15 | # pre_validate method is called before the deployment validation 16 | pre_validate() { 17 | # placeholder for pre deployment validate logic 18 | return $? 19 | } 20 | 21 | # post_validate method is called after the deployment validation 22 | post_validate() { 23 | # placeholder for post deployment validate logic 24 | return $? 25 | } 26 | -------------------------------------------------------------------------------- /IAC/Terraform/terraform/03_config/01_deployment/main.tf: -------------------------------------------------------------------------------- 1 | data "terraform_remote_state" "l02_d01" { 2 | backend = "azurerm" 3 | config = { 4 | resource_group_name = var.rs_resource_group_name 5 | storage_account_name = var.rs_storage_account_name 6 | container_name = var.rs_container_name 7 | key = strcontains(var.rs_container_key, "Test_Mocks") ? var.rs_container_key : "${var.env}/${var.rs_container_key}" #"02_storage/01_deployment" 8 | } 9 | } 10 | 11 | # ------------------------------------------------------------------------------------------------------ 12 | # Deploy resource group 13 | # ------------------------------------------------------------------------------------------------------ 14 | resource "azurecaf_name" "rg_name" { 15 | name = "config" 16 | resource_type = "azurerm_resource_group" 17 | prefixes = [var.env] 18 | random_length = 3 19 | clean_input = true 20 | } 21 | 22 | resource "azurerm_resource_group" "rg" { 23 | name = azurecaf_name.rg_name.result 24 | location = var.location 25 | tags = { 26 | GeneratedBy = "symphony" 27 | } 28 | } 29 | 30 | data "azurerm_client_config" "client_config" { 31 | } 32 | 33 | resource "azurerm_role_assignment" "data_owner_role_assignment" { 34 | scope = azurerm_resource_group.rg.id 35 | role_definition_name = "App Configuration Data Owner" 36 | principal_id = data.azurerm_client_config.client_config.object_id 37 | } 38 | 39 | # ------------------------------------------------------------------------------------------------------ 40 | # Deploy app configuration 41 | # ------------------------------------------------------------------------------------------------------ 42 | resource "azurecaf_name" "app_configuration" { 43 | name = "appconfig" 44 | resource_type = "azurerm_app_configuration" 45 | prefixes = [var.env] 46 | random_length = 3 47 | clean_input = true 48 | } 49 | resource "azurerm_app_configuration" "appconfig" { 50 | name = azurecaf_name.app_configuration.result 51 | location = azurerm_resource_group.rg.location 52 | resource_group_name = azurerm_resource_group.rg.name 53 | sku = "standard" 54 | 55 | depends_on = [ 56 | azurerm_role_assignment.data_owner_role_assignment 57 | ] 58 | } 59 | 60 | # ------------------------------------------------------------------------------------------------------ 61 | # Deploy app config key 62 | # ------------------------------------------------------------------------------------------------------ 63 | 64 | resource "azurerm_app_configuration_key" "app_config_key" { 65 | configuration_store_id = azurerm_app_configuration.appconfig.id 66 | key = "storageAccountName" 67 | value = data.terraform_remote_state.l02_d01.outputs.storage_account_name 68 | content_type = "text/plain" 69 | } 70 | -------------------------------------------------------------------------------- /IAC/Terraform/terraform/03_config/01_deployment/output.tf: -------------------------------------------------------------------------------- 1 | output "resource_group_name" { 2 | value = azurerm_resource_group.rg.name 3 | } 4 | output "app_configuration_id" { 5 | value = azurerm_app_configuration.appconfig.id 6 | } 7 | 8 | output "app_configuration_name" { 9 | value = azurerm_app_configuration.appconfig.name 10 | } 11 | -------------------------------------------------------------------------------- /IAC/Terraform/terraform/03_config/01_deployment/provider.tf: -------------------------------------------------------------------------------- 1 | #Set the terraform required version, and Configure the Azure Provider.Use remote storage 2 | 3 | # Configure the Azure Provider 4 | terraform { 5 | required_version = ">= 1.11.0, < 2.0.0" 6 | backend "azurerm" {} 7 | required_providers { 8 | azurerm = { 9 | source = "hashicorp/azurerm" 10 | version = "=4.1.0" 11 | } 12 | azurecaf = { 13 | source = "aztfmod/azurecaf" 14 | version = "1.2.15" 15 | } 16 | } 17 | } 18 | 19 | provider "azurerm" { 20 | features {} 21 | } 22 | -------------------------------------------------------------------------------- /IAC/Terraform/terraform/03_config/01_deployment/variables.tf: -------------------------------------------------------------------------------- 1 | variable "location" { 2 | description = "The supported azure location where the resource deployed" 3 | type = string 4 | } 5 | 6 | variable "env" { 7 | description = "The name of the environment to be deployed" 8 | type = string 9 | } 10 | 11 | variable "rs_resource_group_name" { 12 | description = "The name of the remote state resource group" 13 | type = string 14 | } 15 | 16 | variable "rs_storage_account_name" { 17 | description = "The name of the remote state storage account" 18 | type = string 19 | } 20 | 21 | variable "rs_container_name" { 22 | description = "The name of the remote state storage account container" 23 | type = string 24 | } 25 | 26 | variable "rs_container_key" { 27 | description = "The name of the remote state file in the storage account container" 28 | type = string 29 | } 30 | -------------------------------------------------------------------------------- /IAC/Terraform/test/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/microsoft/symphony/test 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/Azure/azure-sdk-for-go v50.2.0+incompatible 7 | github.com/gruntwork-io/terratest v0.41.2 8 | github.com/stretchr/testify v1.7.0 9 | ) 10 | -------------------------------------------------------------------------------- /IAC/Terraform/test/terraform/01_init_unit_test.go: -------------------------------------------------------------------------------- 1 | //go:build 01_init || module_test 2 | // +build 01_init module_test 3 | 4 | package terraform 5 | 6 | import ( 7 | "strings" 8 | "testing" 9 | 10 | "github.com/gruntwork-io/terratest/modules/azure" 11 | "github.com/gruntwork-io/terratest/modules/random" 12 | "github.com/stretchr/testify/assert" 13 | 14 | "github.com/gruntwork-io/terratest/modules/terraform" 15 | ) 16 | 17 | func Test01_Init_Storage(t *testing.T) { 18 | t.Parallel() 19 | 20 | uniquePostfix := strings.ToLower(random.UniqueId()) 21 | 22 | // Configure Terraform setting up a path to Terraform code. 23 | terraformOptions := &terraform.Options{ 24 | // The path to where our Terraform code is located 25 | TerraformDir: "../../terraform/01_init", 26 | VarFiles: []string{"terraform.tfvars.json"}, 27 | 28 | Vars: map[string]interface{}{ 29 | "backup_storage_account_name": "remotestatebackup-" + uniquePostfix, 30 | "storage_account_name": "remotestate-" + uniquePostfix, 31 | }, 32 | } 33 | 34 | // Defer 'terraform Destroy' 35 | defer terraform.Destroy(t, terraformOptions) 36 | 37 | // Run `terraform init` to init remote state. 38 | terraform.InitAndApply(t, terraformOptions) 39 | 40 | // Run `terraform output` to get the values of output variables from the terraform.tfstate 41 | resourceGroupName := terraform.Output(t, terraformOptions, "resource_group_name") 42 | storageAccountName := terraform.Output(t, terraformOptions, "storage_account_name") 43 | containerName := terraform.Output(t, terraformOptions, "container_name") 44 | 45 | bkResourceGroupName := terraform.Output(t, terraformOptions, "backup_resource_group_name") 46 | bkStorageAccoutName := terraform.Output(t, terraformOptions, "backup_storage_account_name") 47 | 48 | // assert the resource group, storage account, and container exists 49 | assert.True(t, azure.ResourceGroupExists(t, resourceGroupName, ""), "Primary Resource group does not exist") 50 | assert.True(t, azure.StorageAccountExists(t, storageAccountName, resourceGroupName, ""), "Storage Account does not exist") 51 | assert.True(t, azure.StorageBlobContainerExists(t, containerName, storageAccountName, resourceGroupName, ""), "Container does not exist") 52 | 53 | assert.True(t, azure.ResourceGroupExists(t, bkResourceGroupName, ""), "Backup Resource group does not exist") 54 | assert.True(t, azure.StorageAccountExists(t, bkStorageAccoutName, bkResourceGroupName, ""), "Backup Storage Account does not exist") 55 | } 56 | -------------------------------------------------------------------------------- /IAC/Terraform/test/terraform/02_storage_unit_test.go: -------------------------------------------------------------------------------- 1 | //go:build 02_storage || module_test 2 | // +build 02_storage module_test 3 | 4 | package terraform 5 | 6 | import ( 7 | "os" 8 | "testing" 9 | 10 | "github.com/gruntwork-io/terratest/modules/terraform" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func Test02_Storage(t *testing.T) { 15 | t.Parallel() 16 | 17 | location := "westus" 18 | env := "dev" 19 | //load remote state env vars 20 | rmResourceGroupName := os.Getenv("resource_group_name") 21 | rmStorageAccName := os.Getenv("storage_account_name") 22 | rmContainerName := os.Getenv("container_name") 23 | rmKey := "02_storage/01_deployment_test.tfstate" 24 | 25 | // Configure Terraform setting up a path to Terraform code. 26 | terraformOptions := &terraform.Options{ 27 | // The path to where our Terraform code is located 28 | TerraformDir: "../../terraform/02_storage/01_deployment", 29 | 30 | // Variables to pass to init remote state 31 | BackendConfig: map[string]interface{}{ 32 | "resource_group_name": rmResourceGroupName, 33 | "container_name": rmContainerName, 34 | "storage_account_name": rmStorageAccName, 35 | "key": rmKey}, 36 | 37 | Vars: map[string]interface{}{ 38 | "location": location, 39 | "env": env, 40 | }, 41 | Reconfigure: true, 42 | } 43 | 44 | // Defer 'terraform Destroy' 45 | defer terraform.Destroy(t, terraformOptions) 46 | 47 | // Run `terraform init` to init remote state. 48 | terraform.InitAndApply(t, terraformOptions) 49 | 50 | // Run `terraform output` to get the values of output variables from the terraform.tfstate 51 | resourceGroupName := terraform.Output(t, terraformOptions, "resource_group_name") 52 | storageAccountName := terraform.Output(t, terraformOptions, "storage_account_name") 53 | 54 | // Assert deployed server and databases status 55 | assert.NotEmpty(t, resourceGroupName, "Resource Group Name is empty") 56 | assert.NotEmpty(t, storageAccountName, "Storage Account Name is empty") 57 | } 58 | -------------------------------------------------------------------------------- /IAC/Terraform/test/terraform/03_config_unit_test.go: -------------------------------------------------------------------------------- 1 | //go:build 03_config || module_test 2 | // +build 03_config module_test 3 | 4 | package terraform 5 | 6 | import ( 7 | "os" 8 | "testing" 9 | 10 | "github.com/gruntwork-io/terratest/modules/terraform" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func Test03_Config(t *testing.T) { 15 | t.Parallel() 16 | 17 | //load remote state env vars 18 | rmResourceGroupName := os.Getenv("resource_group_name") 19 | rmStorageAccName := os.Getenv("storage_account_name") 20 | rmContainerName := os.Getenv("container_name") 21 | rmKey := "03_config/01_deployment_test.tfstate" 22 | 23 | // Configure Terraform setting up a path to Terraform code. 24 | terraformOptions := &terraform.Options{ 25 | // The path to where our Terraform code is located 26 | TerraformDir: "../../terraform/03_config/01_deployment", 27 | 28 | // Variables to pass to init remote state 29 | BackendConfig: map[string]interface{}{ 30 | "resource_group_name": rmResourceGroupName, 31 | "container_name": rmContainerName, 32 | "storage_account_name": rmStorageAccName, 33 | "key": rmKey}, 34 | Vars: map[string]interface{}{ 35 | "rs_container_key": "Test_Mocks/02_storage/01_deployment.tfstate", 36 | }, 37 | Reconfigure: true, 38 | } 39 | 40 | // Defer 'terraform Destroy' 41 | defer terraform.Destroy(t, terraformOptions) 42 | 43 | // Run `terraform init` to init remote state. 44 | terraform.InitAndApply(t, terraformOptions) 45 | 46 | // Run `terraform output` to get the values of output variables from the terraform.tfstate 47 | resourceGroupName := terraform.Output(t, terraformOptions, "resource_group_name") 48 | appConfigId := terraform.Output(t, terraformOptions, "app_configuration_id") 49 | appConfigName := terraform.Output(t, terraformOptions, "app_configuration_name") 50 | 51 | // Assert output values are not empty 52 | assert.NotEmpty(t, resourceGroupName, "Resource Group Name is empty") 53 | assert.NotEmpty(t, appConfigId, "App Configuration ID is empty") 54 | assert.NotEmpty(t, appConfigName, "App Configuration Name is empty") 55 | } 56 | -------------------------------------------------------------------------------- /IAC/Terraform/test/terraform/end_to_end_test.go: -------------------------------------------------------------------------------- 1 | //go:build e2e_test 2 | // +build e2e_test 3 | 4 | package terraform 5 | 6 | import ( 7 | "os" 8 | "testing" 9 | 10 | "github.com/gruntwork-io/terratest/modules/terraform" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func Test_EndToEnd(t *testing.T) { 15 | t.Parallel() 16 | 17 | //load remote state env vars 18 | rmResourceGroupName := os.Getenv("resource_group_name") 19 | rmStorageAccName := os.Getenv("storage_account_name") 20 | rmContainerName := os.Getenv("container_name") 21 | envName := os.Getenv("ENVIRONMENT_NAME") + "/" 22 | 23 | // Configure Terraform setting up a path to Terraform code. 24 | terraformOptions02 := &terraform.Options{ 25 | // The path to where our Terraform code is located 26 | TerraformDir: "../../terraform/02_storage/01_deployment", 27 | 28 | // Variables to pass to init remote state 29 | BackendConfig: map[string]interface{}{ 30 | "resource_group_name": rmResourceGroupName, 31 | "container_name": rmContainerName, 32 | "storage_account_name": rmStorageAccName, 33 | "key": envName + "02_storage/01_deployment.tfstate"}, 34 | Reconfigure: true, 35 | } 36 | terraformOptions03 := &terraform.Options{ 37 | // The path to where our Terraform code is located 38 | TerraformDir: "../../terraform/03_config/01_deployment", 39 | 40 | // Variables to pass to init remote state 41 | BackendConfig: map[string]interface{}{ 42 | "resource_group_name": rmResourceGroupName, 43 | "container_name": rmContainerName, 44 | "storage_account_name": rmStorageAccName, 45 | "key": envName + "03_config/01_deployment.tfstate"}, 46 | Reconfigure: true, 47 | } 48 | 49 | //Run `terraform init` to init remote state. 50 | terraform.InitE(t, terraformOptions02) 51 | terraform.InitE(t, terraformOptions03) 52 | 53 | // Run `terraform output` to get the values of output variables from the terraform.tfstate 54 | resourceGroupName := terraform.Output(t, terraformOptions02, "resource_group_name") 55 | storageAccountName := terraform.Output(t, terraformOptions02, "storage_account_name") 56 | configResourceGroupName := terraform.Output(t, terraformOptions03, "resource_group_name") 57 | configId := terraform.Output(t, terraformOptions03, "app_configuration_id") 58 | configName := terraform.Output(t, terraformOptions03, "app_configuration_name") 59 | 60 | // Check that the output values are not empty 61 | assert.NotEmpty(t, resourceGroupName, "Resource Group Name is empty") 62 | assert.NotEmpty(t, storageAccountName, "Storage Account Name is empty") 63 | assert.NotEmpty(t, configResourceGroupName, "Config Resource Group Name is empty") 64 | assert.NotEmpty(t, configId, "App Configuration ID is empty") 65 | assert.NotEmpty(t, configName, "App Configuration Name is empty") 66 | } 67 | -------------------------------------------------------------------------------- /IAC/Terraform/test/terraform/mocked_deployment.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "terraform_version": "1.1.7", 4 | "serial": 2, 5 | "lineage": "97042759-3566-794d-5515-4c01aabad974", 6 | "outputs": { 7 | "resource_group_name": { 8 | "value": "dev-rg-storage-lhp", 9 | "type": "string" 10 | }, 11 | "storage_account_name": { 12 | "value": "dev-sa-storage-vvr", 13 | "type": "string" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | - Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | - Full paths of source file(s) related to the manifestation of the issue 23 | - The location of the affected source code (tag/branch/commit or direct URL) 24 | - Any special configuration required to reproduce the issue 25 | - Step-by-step instructions to reproduce the issue 26 | - Proof-of-concept or exploit code (if possible) 27 | - Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to use Symphony 4 | 5 | Symphony is a multi-IAC, multi-orchestrator framework that encapsulates best practices and guidelines for developing, testing, and deploying your infrastructure as code to host large-scale solutions on Azure. For more details on how to use it, Check the [Getting Started Document](docs/GETTING_STARTED.md) 6 | 7 | ## How to file issues and get help 8 | 9 | This project uses GitHub Issues to track bugs and feature requests. Bugs and feature requests should be documented in a new issue. Before filing a new issue, please search the existing issues list to avoid duplication. 10 | 11 | To learn more about Symphony, please check the project's [docs](docs/) and review the [Contribution Guide](CONTRIBUTING.md). 12 | 13 | ## Microsoft Support Policy 14 | 15 | Support for Symphony is limited to the resources listed above. 16 | -------------------------------------------------------------------------------- /docs/GETTING_STARTED_VM.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Symphony and self hosted build agents on a Virtual Machine 2 | 3 | ## Agent Configuration 4 | 5 | A virtual machine can be either Linux or Windows (with WSL enabled.) It is recommended to run the self-hosted agent as a service, and to run as root. The agent can be configured as root using the following commands. 6 | 7 | ```bash 8 | ./config.sh 9 | sudo ./svc.sh install root 10 | sudo ./svc.sh start 11 | ``` 12 | 13 | ## Windows (WSL) 14 | 15 | If running a self hosted agent on a windows server with WSL. Please complete the following steps. 16 | 17 | ```powershell 18 | # Open a PowerShell window. 19 | 20 | # Fetch the DNS Server IP Address. Note the IP address in the result of this command. 21 | Get-DnsClientServerAddress -InterfaceAlias "Ethernet" -AddressFamily "IPv4" 22 | 23 | # Launch WSL 24 | wsl.exe 25 | ``` 26 | 27 | ```bash 28 | # Edit or create /etc/wsl.conf 29 | sudo nano /etc/wsl.conf 30 | 31 | # Add the following lines to /etc/wsl.conf 32 | [network] 33 | generateResolvConf = false 34 | 35 | # Save the file and exit 36 | # Edit /etc/resolv.conf 37 | sudo nano /etc/resolv.conf 38 | 39 | #Add the following line 40 | nameserver 41 | 42 | # Save the file and exit 43 | ``` 44 | -------------------------------------------------------------------------------- /docs/SAMPLE_APP_ARCHITECTURE.md: -------------------------------------------------------------------------------- 1 | # Sample App Architecture 2 | 3 | This repo has the following components to deploy the sample app using Symphony: 4 | 5 | - [Storage Account](https://azure.microsoft.com/en-us/products/storage/blobs/) 6 | - [App Configuration](https://azure.microsoft.com/en-us/products/app-configuration/) 7 | - [Symphony layers (Terraform or Bicep) to deploy a Storage account and an App Configuration service](./../IAC/) 8 | - Tests to perform tests for symphony layers 9 | - [golang (Terraform)](./../IAC/Terraform/test) 10 | - [Pester (Bicep)](./../IAC/Bicep/test) 11 | - [DevContainer to develop Symphony](DEVELOPER_EXPERIENCE.md) 12 | 13 | ## Symphony Layers (Terraform) 14 | 15 | There are three Symphony layers to deploy the Sample App; 16 | 17 | - [Init layer](./../IAC/Terraform/terraform/01_init) 18 | - [Storage layer](./../IAC/Terraform/terraform/02_storage) 19 | - [Config layer](./../IAC/Terraform/terraform/03_config) 20 | 21 | ### Layout of the symphony layers and deployments (Terraform) 22 | 23 | ```mermaid 24 | graph TD 25 | A[init layer] --> B[storage layer] --> C[config layer] 26 | ``` 27 | 28 | ### Init Layer (Terraform) 29 | 30 | _01_init_ layer is a _special_ layer, that provisions the symphony infrastructure, for example, an _Azure Storage Account_ to hold remote state of terraform modules. 31 | 32 | - Storage Account 33 | - Storage Container 34 | - Storage Account for backup 35 | 36 | ### Storage layer (Terraform) 37 | 38 | _02_storage_ layer has only one deployment: _01_deployment_, which provisions the following resources: 39 | 40 | - Storage Account 41 | 42 | ### Config layer (Terraform) 43 | 44 | _03_config_ layer has only one deployment: _01_deployment_, which provisions the following resources: 45 | 46 | - App Configuration 47 | 48 | ## Symphony Layers (Bicep) 49 | 50 | There are two Symphony layers to deploy the Sample App 51 | 52 | - [Storage layer](./../IAC/Bicep/bicep/01_storage) 53 | - [Config layer](./../IAC/Bicep/bicep/02_config) 54 | 55 | ### Layout of the symphony layers and deployments (Bicep) 56 | 57 | ```mermaid 58 | graph TD 59 | a[storage layer] --> b[config layer] 60 | ``` 61 | 62 | ### Database layer (Bicep) 63 | 64 | _01_storage_ layer has two deployments: 65 | 66 | - _01_rg_, that provisions resource group 67 | - _02_deployment_, which provisions the following resources: 68 | - Storage Account 69 | 70 | ### Config layer (Bicep) 71 | 72 | _02_config_ layer has two deployments: 73 | 74 | - _01_rg_, that provisions resource group 75 | - _02_deployment_, which provisions the following resources: 76 | - App configuration 77 | -------------------------------------------------------------------------------- /docs/TESTING.md: -------------------------------------------------------------------------------- 1 | # IaC Testing 2 | 3 | Like any code, IaC changes can introduce errors, failures, or breaking changes. Automated tests are an essential component of IaC to ensure code quality and reduce the risk of deployment failures, downtime, and associated costs. By detecting errors, failures, or breaking changes early in the process, automated tests can improve the readiness of deployed resources and ensure better service stability. 4 | 5 | Symphony offers samples that help to write and execute both module and end-to-end tests for IaC module code and demonstrate how the tests can be integrated into the symphony workflows. 6 | 7 | ## Module tests 8 | 9 | Module tests ensure that module code/configuration will create the resources successfully. 10 | 11 | - [X] Module tests deploy the module resources, then validate the deployed resources & configurations, and finally tear down any deployed resources. 12 | - [X] Slow to execute, but can be executed in parallel. 13 | - [X] Have no dependency on any resource other than the module under test resources. 14 | 15 | ## End to End tests 16 | 17 | End to End tests ensure that all resources deployed by one or more modules are working as expected. 18 | 19 | - [X] End to End tests validate already deployed resources for a long-lived environment e.g. development or production 20 | - [X] Fast to execute, and can be executed in parallel. 21 | - [X] Depend on multiple modules, and sometimes the entire system resources. 22 | 23 | ## Bicep 24 | 25 | All below test examples have an assumption for the working directory - should be [IAC/Bicep/test](./../IAC/Bicep/test) 26 | 27 | ### Azure Resource Manager Template Toolkit (arm-ttk) 28 | 29 | The tests will check a template or set of templates for coding best practices using [arm-ttk](https://github.com/Azure/arm-ttk). 30 | 31 | 1. Generate ARM template based on Bicep 32 | 33 | ```powershell 34 | az bicep build --file ../bicep/01_storage/02_deployment/main.bicep 35 | ``` 36 | 37 | 1. Install ARM TTK module 38 | 39 | ```powershell 40 | Import-Module .\arm-ttk\arm-ttk.psd1 41 | ``` 42 | 43 | 1. Run the test 44 | 45 | ```powershell 46 | Test-AzTemplate -TemplatePath ../bicep/01_storage/02_deployment/main.json 47 | ``` 48 | 49 | 1. Cleanup 50 | 51 | ```powershell 52 | Remove-Item -Force -Path ../bicep/01_storage/02_deployment/main.json 53 | ``` 54 | 55 | ### End to End Tests with Pester 56 | 57 | [Pester](https://pester.dev/docs/quick-start) is a testing and mocking framework for PowerShell. 58 | 59 | 1. Install Pester module 60 | 61 | ```powershell 62 | Install-Module -Name Pester -AllowClobber -Force -Confirm:$False -SkipPublisherCheck 63 | ``` 64 | 65 | 1. (option 1) Run the test 66 | 67 | ```powershell 68 | Invoke-Pester -Path ./pester/End_To_End.Tests.ps1 69 | ``` 70 | 71 | 1. (option 2) Run the test with the JUnit report 72 | 73 | ```powershell 74 | Invoke-Pester -Path ./pester/End_To_End.Tests.ps1 -OutputFile Test.xml -OutputFormat JUnitXml 75 | ``` 76 | 77 | ## Terraform 78 | 79 | All below test examples have an assumption for the working directory - should be [IAC/Terraform/test/terraform](./../IAC/Terraform/test/terraform/) 80 | 81 | ### End to End tests with Terratest 82 | 83 | [Terratest](https://github.com/gruntwork-io/terratest) is a Go library that makes it easier to write automated tests for your infrastructure code. It provides a variety of helper functions and patterns for common infrastructure testing tasks, and offers good support for the most commonly used Azure resources. 84 | 85 | 1. Ensure Go 1.16 is installed, and the [Terratest Go environment](https://github.com/gruntwork-io/terratest/blob/master/examples/azure/README.md) is properly configured. 86 | 87 | 1. Run the tests. 88 | 89 | ```bash 90 | go test -v -timeout 1000s --tags=e2e_test 91 | ``` 92 | -------------------------------------------------------------------------------- /docs/images/PR_workflow.drawio: -------------------------------------------------------------------------------- 1 | 7Vpbb+I6EP41PG6VC+HyWFKWXYkeVeVo99nEJvHWiXMcp0B//ZlJHEiastueNgcqRUJgj8e3mc8z4zED1493C0XS6FZSJgaORXcD92bgOLbnWfCDlH1JmbheSQgVpyXJOhJW/ImZnhU155RlhlaStJRC87RJDGSSsEA3aEQpuW2ybaSgDUJKQtYirAIi2tSfnOqopI6rbSH9G+NhZGYeWaYhJhWvIWQRoXJbI7nzgesrKXVZinc+Eyi7pli+nmg9rEuxRL+mQ+Q//lx4879keq2i3dNXtaA/vrhuOcwjEbnZ8MAZCRhwtsY1672Rw+ifHBc628hEf8kKLV0Dgz1JQdOzYzuUQvy9YamQe+BYaRSwGRNWt644jFQOMzjZlseCJMzMsjItFtSDiAu6JHuZ41YzTYKHqjaLpOJPwE8ENNlAgGalDZBcq8Gxwp5mTMVwG3eV/OxnpFuyazAuSaar1UghSJrxdbE+7BgTFfJkJrWWsWHaRlyzVUoC5NnC2cCF6LhapJJ5Qhk1tQ0XwpdCqkISLiVssgmKrSj5wGoto2DC1hvTo5JQJgUquZRauW97cpBwHSEGNI9MabarkQxiFkzGTCtUW9U6MV32Vd2geXs8C45lTnRUOwdTw0fM8QsPQx8hCgWD0rcgdthC7AFrpOguyJ6pDM+6Am3gSeRJC2ywf90SMGUbkhdArSvkSCWChwlQBNtgFcXIwVJcG3LMKcXxZxmonSfhsmC7GR4p90ZASJLQfSMKixBBRwYjzFLJE11IzJvBB2ToW1fewIMV+1C3j3X4ILvSvkxgE7BFHJcBSLcMgQoA00ST9eEIvQmPbeT83nr8GU8GP+4r4eN2Bp/RW+Fzdw9fa0WSILrqYXQZMBqdHUbjd/rN8Qm/+QPAQYlmvec8l+ccf4znrMK+fbP6J8TaTmeQnbYge5+DY7QWXMP3kpGHDGcAZLEgV1zjZrKAQFhdmEOZGGPYm8AOTOD41fi6FE86dE7g6Tvx0Y2CHAvg+CUPWjUOEq4jybjVHlKXAamze9WhfQJSS4zJ8OYP4jsgKIhIEoJUIMKXNBesbBD7Hk6XAafp2eH0UnLjGTZqYjkkaFCElGRRISKrhoWAoU37LRoikuLI8S7EdNhVmYFyrtYsoddYxqFRPraHJZSji1f1ROogqmZDvm+M4Oye92LE8zx+eRu0KVcsQGNcjJCjcj4k4nGbIU+VOqjpv8oeNDIF067U384U9OrvTv3utJkqcobt8///6r+rO9rfaHX7+9nnvp+546a1cqv70dkym8NTF7S5M69inz4D1UlwM/x816/qAaqGlrt8LXgWGazAD5xv0HfWY+YyMHP2+5XXvl/1EVF3EZH37PHMfeHxbPSSh+kqIvLaKZtaRFQ93KLs+pfbzx7feHYTfMPxK+ObUWfWp30dvyVJToqOaEceYbe8SO38khiha4nLSFMF3gJKtEJm780+3psdTMMnioC8Fp6Oxqt8y6XF026RIwRbIHMVsD4aei9+3m2KvNdetf4DdKB6/EtV0Vb7X5o7/xc= 2 | -------------------------------------------------------------------------------- /docs/images/destroy_workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/symphony/26caa6e2acf523441aae17e98d2b7c62bcafc0cf/docs/images/destroy_workflow.png -------------------------------------------------------------------------------- /docs/images/environment.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/symphony/26caa6e2acf523441aae17e98d2b7c62bcafc0cf/docs/images/environment.PNG -------------------------------------------------------------------------------- /docs/images/pr_workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/symphony/26caa6e2acf523441aae17e98d2b7c62bcafc0cf/docs/images/pr_workflow.png -------------------------------------------------------------------------------- /docs/images/symphony.drawio: -------------------------------------------------------------------------------- 1 | 7ZnbcuI4EIafhstMGXMIXE5IMpPa3arskJm9VuzGViFLWlkOsE+/3ZJ8AJOabGo2hKpwkVitc/+f2i0YjBbF9othOv9DpSAGcZRuB6PrQRwPh1GM/8iy85bZ5NIbMsPT0Kg1LPk/EIxRsFY8hXKvoVVKWK73jYmSEhK7Z2PGqM1+s5US+7NqlkHPsEyY6Fv/4qnN611Erf0r8Cy3zYZDzSNL1plRlQzzSSXB1xSsHiY0LXOWqk3HNLoZjBZGKeufiu0CBLm19pjvd/tMbbNkA9K+pMNvs3Whlo839uvFw5+z8f33u210EUZ5YqIKrvjBBE+ZDbso7a72Ea5f02NpQQ9GVxoML8CCCab7tny1ybmFpWYJtd8gMmjLbSGwNMTHFd9CDQGVU25QVK4kDaUq8hu2EWKhhDJu8lEUXU5uZmgvrVFrqGucu7GxkvaWFVwQfkvIFK4++n4XauqZpnU57GkYyp1pbt0H7X3n1p4CY2HbMQVnfwGFuzc7bBJqZ0H33X5x0+ErmPIuWjUuLCCdNQO32uJDkPc/SB33pL438MRh896Unl4uFues9HB+aqlHPamvQQu1e3dKx4vxOSs9ik6t9Lin9AOU9r3pPJlfRees83h4ap0nPZ2/gVbm3Sk9vp7Pz1npSXxqpWf9E52DN4S8DOWmdDaOQJaVwbSZ8uKUDAZYyiWU5Scs3FmyVLJsO6N4VGKYrcaR4NJymYVUG+0LGjphsjPgCl2ODlZlyR8FGRIDKTqbM9qHALYO/fyQsIWksuDnICdXktMq0GRpTQesokR2n7l9brp5X5e0wBPuKSMYBaxoGNKcY0r/OZgLnqY001HOXcoOpEN0wN+sz1vkPj8nuENsc+5oXvKyn6j0pd/diq+Hk9b0LbDlbL+A6+H0p+nnNDoGdvR/gT1/BmxdZ6EN11p4CK2rv/u8aIFMciYzaPnmCBjCiNoddiCoaUwQGLVQaYdpB1CaBPexyZm94Cs3Q1Egxm4cRXdZlC+xzahlonRnBV3sdfUoeJl3lkCDS3+48IBWwpbeo9SD/iCpK5bYVx6JkCCf2ZE4HfvHEvI3hr9OIHr0pyExb+D3hmfwd2AyrcWuqW94jFZGFS2B+6eqOTDtOyNgXSUJvjBWFW2h0vSGoUq1alpga1WZBF4bwEOS/0HrC2k9dql4a1r7XwsN4qmwLgt8wseMHj3A1t032pykjbAQ1/Uu8GWMy7KNp55zCswtYSFIHyDqqTxoiDGU6jbKrH2cddEVtroT7X10xkHk0SAdIvOnemvoqs7uXgd7uOl8wP5C2I/drN4a9v4XYw85LztMZyDBhMDo0ZEAqSOyTAzXtskZDGijDhAv0FuB7MBgG7sxYa/JptvcXlZBXy1X2i/Dpf0IlVuCy1YM/F3hhSx9ZVAO97QPTl/I6bF74S/jFIvtzwCurvMzy+jmXw== 2 | -------------------------------------------------------------------------------- /docs/images/workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/symphony/26caa6e2acf523441aae17e98d2b7c62bcafc0cf/docs/images/workflow.png -------------------------------------------------------------------------------- /env/bicep/dev/01_storage/01_rg/parameters.bicepparam: -------------------------------------------------------------------------------- 1 | using './../../../../../IAC/Bicep/bicep/01_storage/01_rg/main.bicep' 2 | 3 | param environment = readEnvironmentVariable('ENVIRONMENT_NAME') 4 | param location = readEnvironmentVariable('LOCATION_NAME', 'westus') 5 | param layerName = readEnvironmentVariable('layerName') 6 | -------------------------------------------------------------------------------- /env/bicep/dev/01_storage/02_deployment/parameters.bicepparam: -------------------------------------------------------------------------------- 1 | using './../../../../../IAC/Bicep/bicep/01_storage/02_deployment/main.bicep' 2 | 3 | param environment = readEnvironmentVariable('ENVIRONMENT_NAME') 4 | param location = readEnvironmentVariable('LOCATION_NAME', 'westus') 5 | param layerName = readEnvironmentVariable('layerName') 6 | -------------------------------------------------------------------------------- /env/bicep/dev/02_config/01_rg/parameters.bicepparam: -------------------------------------------------------------------------------- 1 | using './../../../../../IAC/Bicep/bicep/02_config/01_rg/main.bicep' 2 | 3 | param environment = readEnvironmentVariable('ENVIRONMENT_NAME') 4 | param location = readEnvironmentVariable('LOCATION_NAME', 'westus') 5 | param layerName = readEnvironmentVariable('layerName') 6 | -------------------------------------------------------------------------------- /env/bicep/dev/02_config/02_deployment/parameters.bicepparam: -------------------------------------------------------------------------------- 1 | using './../../../../../IAC/Bicep/bicep/02_config/02_deployment/main.bicep' 2 | 3 | param environment = readEnvironmentVariable('ENVIRONMENT_NAME') 4 | param location = readEnvironmentVariable('LOCATION_NAME', 'westus') 5 | param layerName = readEnvironmentVariable('layerName') 6 | 7 | param storageAccountName = readEnvironmentVariable('storageAccountName') 8 | -------------------------------------------------------------------------------- /env/bicep/pr/01_storage/01_rg/parameters.bicepparam: -------------------------------------------------------------------------------- 1 | using './../../../../../IAC/Bicep/bicep/01_storage/01_rg/main.bicep' 2 | 3 | param environment = readEnvironmentVariable('ENVIRONMENT_NAME') 4 | param location = readEnvironmentVariable('LOCATION_NAME', 'westus') 5 | param layerName = readEnvironmentVariable('layerName') 6 | -------------------------------------------------------------------------------- /env/bicep/pr/01_storage/02_deployment/parameters.bicepparam: -------------------------------------------------------------------------------- 1 | using './../../../../../IAC/Bicep/bicep/01_storage/02_deployment/main.bicep' 2 | 3 | param environment = readEnvironmentVariable('ENVIRONMENT_NAME') 4 | param location = readEnvironmentVariable('LOCATION_NAME', 'westus') 5 | param layerName = readEnvironmentVariable('layerName') 6 | -------------------------------------------------------------------------------- /env/bicep/pr/02_config/01_rg/parameters.bicepparam: -------------------------------------------------------------------------------- 1 | using './../../../../../IAC/Bicep/bicep/02_config/01_rg/main.bicep' 2 | 3 | param environment = readEnvironmentVariable('ENVIRONMENT_NAME') 4 | param location = readEnvironmentVariable('LOCATION_NAME', 'westus') 5 | param layerName = readEnvironmentVariable('layerName') 6 | -------------------------------------------------------------------------------- /env/bicep/pr/02_config/02_deployment/parameters.bicepparam: -------------------------------------------------------------------------------- 1 | using './../../../../../IAC/Bicep/bicep/02_config/02_deployment/main.bicep' 2 | 3 | param environment = readEnvironmentVariable('ENVIRONMENT_NAME') 4 | param location = readEnvironmentVariable('LOCATION_NAME', 'westus') 5 | param layerName = readEnvironmentVariable('layerName') 6 | 7 | param storageAccountName = readEnvironmentVariable('storageAccountName') 8 | -------------------------------------------------------------------------------- /env/bicep/prod/01_storage/01_rg/parameters.bicepparam: -------------------------------------------------------------------------------- 1 | using './../../../../../IAC/Bicep/bicep/01_storage/01_rg/main.bicep' 2 | 3 | param environment = readEnvironmentVariable('ENVIRONMENT_NAME') 4 | param location = readEnvironmentVariable('LOCATION_NAME', 'westus') 5 | param layerName = readEnvironmentVariable('layerName') 6 | -------------------------------------------------------------------------------- /env/bicep/prod/01_storage/02_deployment/parameters.bicepparam: -------------------------------------------------------------------------------- 1 | using './../../../../../IAC/Bicep/bicep/01_storage/02_deployment/main.bicep' 2 | 3 | param environment = readEnvironmentVariable('ENVIRONMENT_NAME') 4 | param location = readEnvironmentVariable('LOCATION_NAME', 'westus') 5 | param layerName = readEnvironmentVariable('layerName') 6 | -------------------------------------------------------------------------------- /env/bicep/prod/02_config/01_rg/parameters.bicepparam: -------------------------------------------------------------------------------- 1 | using './../../../../../IAC/Bicep/bicep/02_config/01_rg/main.bicep' 2 | 3 | param environment = readEnvironmentVariable('ENVIRONMENT_NAME') 4 | param location = readEnvironmentVariable('LOCATION_NAME', 'westus') 5 | param layerName = readEnvironmentVariable('layerName') 6 | -------------------------------------------------------------------------------- /env/bicep/prod/02_config/02_deployment/parameters.bicepparam: -------------------------------------------------------------------------------- 1 | using './../../../../../IAC/Bicep/bicep/02_config/02_deployment/main.bicep' 2 | 3 | param environment = readEnvironmentVariable('ENVIRONMENT_NAME') 4 | param location = readEnvironmentVariable('LOCATION_NAME', 'westus') 5 | param layerName = readEnvironmentVariable('layerName') 6 | 7 | param storageAccountName = readEnvironmentVariable('storageAccountName') 8 | -------------------------------------------------------------------------------- /env/terraform/dev/01_init.tfvars.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": "dev", 3 | "env_version": "0.0.1", 4 | "location": "westus", 5 | "resource_group_name": "rg-symphotfstate-dev", 6 | "storage_account_name": "stsymphotfstatedev", 7 | "storage_account_tier": "Standard", 8 | "storage_account_replication_type": "LRS", 9 | "storage_account_kind": "StorageV2", 10 | "identity_type": "SystemAssigned", 11 | "container_name": "tfstate", 12 | "backup_resource_group_name": "rg-symphotfstate-bak-dev", 13 | "backup_storage_account_name": "stsymphotfstatebakdev" 14 | } 15 | -------------------------------------------------------------------------------- /env/terraform/dev/02_storage_01_deployment.tfvars.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": "dev", 3 | "location": "westus" 4 | } 5 | -------------------------------------------------------------------------------- /env/terraform/dev/03_config_01_deployment.tfvars.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": "dev", 3 | "location": "westus", 4 | "rs_resource_group_name": "devops-symphony-362", 5 | "rs_storage_account_name": "symphonyremotestate360", 6 | "rs_container_name": "tfstate", 7 | "rs_container_key": "02_storage/01_deployment.tfstate" 8 | } 9 | -------------------------------------------------------------------------------- /env/terraform/pr/01_init.tfvars.json: -------------------------------------------------------------------------------- 1 | { 2 | "env_version": "0.0.1", 3 | "location": "westus", 4 | "resource_group_name": "rg-symphotfstate-pr", 5 | "storage_account_name": "stsymphotfstatepr", 6 | "storage_account_tier": "Standard", 7 | "storage_account_replication_type": "LRS", 8 | "storage_account_kind": "StorageV2", 9 | "identity_type": "SystemAssigned", 10 | "container_name": "tfstate", 11 | "backup_resource_group_name": "rg-symphotfstate-bak-pr", 12 | "backup_storage_account_name": "stsymphotfstatebakpr" 13 | } 14 | -------------------------------------------------------------------------------- /env/terraform/pr/02_storage_01_deployment.tfvars.json: -------------------------------------------------------------------------------- 1 | { 2 | "location": "westus" 3 | } 4 | -------------------------------------------------------------------------------- /env/terraform/pr/03_config_01_deployment.tfvars.json: -------------------------------------------------------------------------------- 1 | { 2 | "location": "westus", 3 | "rs_resource_group_name": "devops-symphony-362", 4 | "rs_storage_account_name": "symphonyremotestate360", 5 | "rs_container_name": "tfstate", 6 | "rs_container_key": "02_storage/01_deployment.tfstate" 7 | } 8 | -------------------------------------------------------------------------------- /env/terraform/prod/01_init.tfvars.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": "dev", 3 | "env_version": "0.0.1", 4 | "location": "westus", 5 | "resource_group_name": "rg-symphotfstate-dev", 6 | "storage_account_name": "stsymphotfstatedev", 7 | "storage_account_tier": "Standard", 8 | "storage_account_replication_type": "LRS", 9 | "storage_account_kind": "StorageV2", 10 | "identity_type": "SystemAssigned", 11 | "container_name": "tfstate", 12 | "backup_resource_group_name": "rg-symphotfstate-bak-dev", 13 | "backup_storage_account_name": "stsymphotfstatebakdev" 14 | } 15 | -------------------------------------------------------------------------------- /env/terraform/prod/02_storage_01_deployment.tfvars.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": "prod", 3 | "location": "westus" 4 | } 5 | -------------------------------------------------------------------------------- /env/terraform/prod/03_config_01_deployment.tfvars.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": "prod", 3 | "location": "westus", 4 | "rs_resource_group_name": "devops-symphony-362", 5 | "rs_storage_account_name": "symphonyremotestate360", 6 | "rs_container_name": "tfstate", 7 | "rs_container_key": "02_storage/01_deployment.tfstate" 8 | } 9 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source scripts/utilities/shell_logger.sh 4 | source scripts/utilities/shell_inputs.sh 5 | source scripts/utilities/http.sh 6 | source scripts/utilities/service_principal.sh 7 | source scripts/install/banner.sh 8 | source scripts/install/contents.sh 9 | 10 | #script name 11 | declare me=$(basename "$0") 12 | 13 | declare ORCHESTRATOR=$1 14 | declare IACTOOL=$2 15 | 16 | main() { 17 | _information "This install script deprecated, please source setup.sh and use the symphony cli" 18 | 19 | source ./setup.sh 20 | symphony pipeline config "$ORCHESTRATOR" "$IACTOOL" 21 | } 22 | 23 | # Entry point 24 | main 25 | -------------------------------------------------------------------------------- /scripts/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /scripts/install/banner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function show_banner { 4 | cat <<"EOF" 5 | 6 | .d88888b dP 7 | 88. "' 88 8 | `Y88888b. dP dP 88d8b.d8b. 88d888b. 88d888b. .d8888b. 88d888b. dP dP 9 | `8b 88 88 88'`88'`88 88' `88 88' `88 88' `88 88' `88 88 88 10 | d8' .8P 88. .88 88 88 88 88. .88 88 88 88. .88 88 88 88. .88 11 | Y88888P `8888P88 dP dP dP 88Y888P' dP dP `88888P' dP dP `8888P88 12 | .88 88 .88 13 | d8888P dP d8888P 14 | 15 | EOF 16 | 17 | echo "" 18 | } 19 | -------------------------------------------------------------------------------- /scripts/install/contents.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | INSTALL_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd) 3 | source "$INSTALL_DIR"/../utilities/shell_logger.sh 4 | 5 | # TARGET_ROOT should be fined in the calling script that sources this utilities file. 6 | 7 | function remove_yaml() { 8 | if [ "$ORCHESTRATOR" == "azdo" ]; then 9 | rm -r "$TARGET_ROOT"/.github/* 10 | else 11 | rm -r "$TARGET_ROOT"/.azure-pipelines/* 12 | rm "$TARGET_ROOT"/.github/CODEOWNERS 13 | fi 14 | } 15 | function remove_tf_content() { 16 | _information "Remove Terraform IaC modules" 17 | rm -r "$TARGET_ROOT"/IAC/Terraform/* 18 | rm -r "$TARGET_ROOT"/IAC/Terraform 19 | 20 | _information "Remove Terraform env" 21 | rm -r "$TARGET_ROOT"/env/terraform/* 22 | rm -r "$TARGET_ROOT"/env/terraform 23 | 24 | if [ "$ORCHESTRATOR" == "azdo" ]; then 25 | _information "Remove Terraform Azdo pipeline" 26 | rm "$TARGET_ROOT"/.azure-pipelines/*.terraform*.yml 27 | else 28 | _information "Remove Terraform GitHub workflows" 29 | rm "$TARGET_ROOT"/.github/workflows/*.terraform*.yml 30 | fi 31 | 32 | _information "Remove Terraform orchestrators scripts" 33 | rm "$TARGET_ROOT"/scripts/orchestrators/*.tf.*.sh 34 | rm "$TARGET_ROOT"/scripts/orchestrators/*terraform*.sh 35 | rm "$TARGET_ROOT"/scripts/orchestrators/*tflint.sh 36 | } 37 | 38 | function remove_bicep_content() { 39 | 40 | _information "Remove Bicep IaC modules" 41 | rm -r "$TARGET_ROOT"/IAC/Bicep/* 42 | rm -r "$TARGET_ROOT"/IAC/Bicep 43 | 44 | _information "Remove Bicep env" 45 | rm -r "$TARGET_ROOT"/env/bicep/* 46 | rm -r "$TARGET_ROOT"/env/bicep 47 | 48 | if [ "$ORCHESTRATOR" == "azdo" ]; then 49 | _information "Remove Bicep Azdo pipeline" 50 | rm "$TARGET_ROOT"/.azure-pipelines/*.bicep*.yml 51 | else 52 | _information "Remove Bicep GitHub workflows" 53 | rm "$TARGET_ROOT"/.github/workflows/*.bicep*.yml 54 | fi 55 | 56 | _information "Remove Bicep orchestrators scripts" 57 | rm "$TARGET_ROOT"/scripts/orchestrators/*bicep*.sh 58 | rm "$TARGET_ROOT"/scripts/orchestrators/*powershell*.sh 59 | rm "$TARGET_ROOT"/scripts/orchestrators/*pester.sh 60 | rm "$TARGET_ROOT"/scripts/orchestrators/setup-armttk.sh 61 | } 62 | 63 | function remove_tmp_terraform() { 64 | _information "Remove temporary Terraform files" 65 | rm -r "$TARGET_ROOT"/IAC/Terraform/test/terraform/mocked_deployment.tfstate 66 | } 67 | 68 | function delete_sample_code() { 69 | local iac_tool=$1 70 | 71 | _information "Remove sample code" 72 | 73 | if [ "$iac_tool" == "bicep" ]; then 74 | rm -rf "$TARGET_ROOT"/IAC/Bicep/* 75 | touch "$TARGET_ROOT"/IAC/Bicep/.gitkeep 76 | 77 | rm -rf "$TARGET_ROOT"/env/bicep/* 78 | 79 | # create the well-known PR dir 80 | mkdir -p "$TARGET_ROOT"/env/bicep/pr 81 | touch "$TARGET_ROOT"/env/bicep/pr/.gitkeep 82 | else 83 | rm -rf "$TARGET_ROOT"/IAC/Terraform/* 84 | touch "$TARGET_ROOT"/IAC/Terraform/.gitkeep 85 | 86 | rm -rf "$TARGET_ROOT"/env/terraform/* 87 | 88 | # create the well-known PR dir 89 | mkdir -p "$TARGET_ROOT"/env/terraform/pr 90 | touch "$TARGET_ROOT"/env/terraform/pr/.gitkeep 91 | fi 92 | } 93 | -------------------------------------------------------------------------------- /scripts/install/providers/azdo/templates/authorized-resources.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "endpoint", 4 | "id": "__SERVICE_CONNECTION_ID__", 5 | "authorized": true 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /scripts/install/providers/azdo/templates/pipeline-authorize.json: -------------------------------------------------------------------------------- 1 | { 2 | "pipelines": [ 3 | { 4 | "id": __PIPELINE_ID__, 5 | "authorized": true 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /scripts/install/providers/azdo/templates/pipeline-create.json: -------------------------------------------------------------------------------- 1 | { 2 | "process": { 3 | "yamlFilename": "__ADO_PIPELINE_YAML_FILE_PATH__", 4 | "type": 2, 5 | "resources": {}, 6 | "target": null 7 | }, 8 | "repository": { 9 | "type": "TfsGit", 10 | "name": "__ADO_PIPELINE_REPO_NAME__", 11 | "defaultBranch": "refs/heads/__ADO_PIPELINE_REPO_BRANCH__" 12 | }, 13 | "path": "__ADO_PIPELINE_FOLDER_PATH__", 14 | "name": "__ADO_PIPELINE_NAME__", 15 | "type": "build", 16 | "queueStatus": "enabled", 17 | "triggers": [ 18 | { 19 | "batchChanges": false, 20 | "maxConcurrentBuildsPerBranch": 1, 21 | "pollingJobId": null, 22 | "pollingInterval": 0, 23 | "pathFilters": [], 24 | "branchFilters": [ 25 | "+refs/heads/main" 26 | ], 27 | "defaultSettingsSourceType": 2, 28 | "isSettingsSourceOptionSupported": true, 29 | "settingsSourceType": 2, 30 | "triggerType": 2 31 | } 32 | ], 33 | "variables": { __ADO_PIPELINE_VARIABLES__ 34 | }, 35 | "queue": { 36 | "id": __ADO_POOL_ID__, 37 | "name": "__ADO_POOL_NAME__", 38 | "url": "__AZDO_ORG_URI__/_apis/build/Queues/__ADO_POOL_ID__", 39 | "pool": { 40 | "id": __ADO_POOL_ID__, 41 | "name": "__ADO_POOL_NAME__", 42 | "isHosted": true 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /scripts/install/providers/azdo/templates/pipeline-variable.json: -------------------------------------------------------------------------------- 1 | "__PIPELINE_VAR_NAME__": { 2 | "value": "__PIPELINE_VAR_VALUE__", 3 | "isSecret": __PIPELINE_VAR_IS_SECRET__, 4 | "allowOverride": __PIPELINE_ALLOW_OVERRIDE__ 5 | } 6 | -------------------------------------------------------------------------------- /scripts/install/providers/azdo/templates/project-create.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "__AZDO_PROJECT_NAME__", 3 | "description": "IaC project created by CSE DevOps", 4 | "capabilities": { 5 | "versioncontrol": { 6 | "sourceControlType": "__AZDO_PROJECT_SOURCE_CONTROL__" 7 | }, 8 | "processTemplate": { 9 | "templateTypeId": "__AZDO_PROCESS_TEMPLATE_ID__" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /scripts/install/providers/azdo/templates/sc-ado-auth.json: -------------------------------------------------------------------------------- 1 | { 2 | "resource": { 3 | "type": "endpoint", 4 | "id": "__SC_ADO_ID__" 5 | }, 6 | "allPipelines": { 7 | "authorized": true, 8 | "authorizedBy": null, 9 | "authorizedOn": null 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /scripts/install/providers/azdo/templates/sc-ado-paas-federated.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "scopeLevel": "Subscription", 4 | "environment": "AzureCloud", 5 | "subscriptionId": "__SUBSCRIPTION_ID__", 6 | "subscriptionName": "__SUBSCRIPTION_NAME__" 7 | }, 8 | "name": "__SERVICE_CONNECTION_NAME__", 9 | "description": "Azure", 10 | "type": "azurerm", 11 | "url": "__MANAGEMENT_URI__", 12 | "authorization": { 13 | "parameters": { 14 | "serviceprincipalid": "__SERVICE_PRINCIPAL_ID__", 15 | "tenantid": "__SERVICE_PRINCIPAL_TENANT_ID__" 16 | }, 17 | "scheme": "WorkloadIdentityFederation" 18 | }, 19 | "isShared": false, 20 | "isReady": true, 21 | "serviceEndpointProjectReferences": [ 22 | { 23 | "name": "__SERVICE_CONNECTION_NAME__", 24 | "projectReference": { 25 | "id": "__PROJECT_ID__" 26 | } 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /scripts/install/providers/azdo/templates/sc-ado-paas.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "releaseUrl": "https://vsrm.dev.azure.com/__ADO_ORG_NAME__" 4 | }, 5 | "id": null, 6 | "name": "sc-ado", 7 | "type": "AZDOAPI", 8 | "url": "__ADO_ORG_URI__", 9 | "createdBy": null, 10 | "description": "", 11 | "authorization": { 12 | "parameters": { 13 | "apitoken": "__ADO_PAT__" 14 | }, 15 | "scheme": "Token" 16 | }, 17 | "isShared": false, 18 | "isReady": true, 19 | "owner": "library", 20 | "serviceEndpointProjectReferences": null 21 | } 22 | -------------------------------------------------------------------------------- /scripts/install/providers/azdo/templates/sc-ado-server.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": null, 3 | "name": "sc-ado", 4 | "type": "AZDOAPI", 5 | "url": "__ADO_ORG_URI__", 6 | "createdBy": null, 7 | "description": "", 8 | "authorization": { 9 | "parameters": { 10 | "apitoken": "__ADO_PAT__" 11 | }, 12 | "scheme": "Token" 13 | }, 14 | "isShared": false, 15 | "isReady": true, 16 | "owner": "library", 17 | "serviceEndpointProjectReferences": null 18 | } 19 | -------------------------------------------------------------------------------- /scripts/install/providers/azdo/templates/service-connection-create-paas.json: -------------------------------------------------------------------------------- 1 | { 2 | "administratorsGroup": null, 3 | "authorization": { 4 | "scheme": "ServicePrincipal", 5 | "parameters": { 6 | "serviceprincipalid": "__SERVICE_PRINCIPAL_ID__", 7 | "authenticationType": "spnKey", 8 | "serviceprincipalkey": "__SERVICE_PRINCIPAL_KEY__", 9 | "tenantid": "__SERVICE_PRINCIPAL_TENANT_ID__" 10 | } 11 | }, 12 | "createdBy": null, 13 | "data": { 14 | "environment": "__CLOUD_ENVIRONMENT__", 15 | "subscriptionId": "__SUBSCRIPTION_ID__", 16 | "subscriptionName": "__SUBSCRIPTION_NAME__", 17 | "scopeLevel": "Subscription", 18 | "creationMode": "Manual" 19 | }, 20 | "description": "", 21 | "groupScopeId": null, 22 | "name": "__SERVICE_CONNECTION_NAME__", 23 | "operationStatus": null, 24 | "readersGroup": null, 25 | "serviceEndpointProjectReferences": [ 26 | { 27 | "description": "", 28 | "name": "__SERVICE_CONNECTION_NAME__", 29 | "projectReference": { 30 | "id": "__PROJECT_ID__", 31 | "name": "__PROJECT_NAME__" 32 | } 33 | } 34 | ], 35 | "type": "azurerm", 36 | "url": "__MANAGEMENT_URI__", 37 | "isShared": false, 38 | "owner": "library" 39 | } 40 | -------------------------------------------------------------------------------- /scripts/install/providers/azdo/templates/service-connection-create-server.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "", 3 | "administratorsGroup": null, 4 | "authorization": { 5 | "parameters": { 6 | "tenantid": "__SERVICE_PRINCIPAL_TENANT_ID__", 7 | "serviceprincipalid": "__SERVICE_PRINCIPAL_ID__", 8 | "authenticationType": "spnKey", 9 | "serviceprincipalkey": "__SERVICE_PRINCIPAL_KEY__" 10 | }, 11 | "scheme": "ServicePrincipal" 12 | }, 13 | "createdBy": null, 14 | "data": { 15 | "subscriptionId": "__SUBSCRIPTION_ID__", 16 | "subscriptionName": "__SUBSCRIPTION_NAME__", 17 | "environment": "__CLOUD_ENVIRONMENT__", 18 | "scopeLevel": "Subscription", 19 | "creationMode": "Manual", 20 | "azureSpnRoleAssignmentId": "", 21 | "azureSpnPermissions": "", 22 | "spnObjectId": "", 23 | "appObjectId": "" 24 | }, 25 | "name": "__SERVICE_CONNECTION_NAME__", 26 | "type": "azurerm", 27 | "url": "__MANAGEMENT_URI__", 28 | "readersGroup": null, 29 | "groupScopeId": null, 30 | "operationStatus": null 31 | } 32 | -------------------------------------------------------------------------------- /scripts/install/providers/azdo/templates/variable-group-create.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "__ADO_VARIABLE_GROUP_NAME__", 3 | "type": "Vsts", 4 | "variables": { 5 | __ADO_VARIABLES__ 6 | }, 7 | "variableGroupProjectReferences": [ 8 | { 9 | "name": "__ADO_VARIABLE_GROUP_NAME__", 10 | "projectReference": { 11 | "id": "__ADO_PROJECT_ID__", 12 | "name": "__ADO_PROJECT_NAME__" 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /scripts/orchestrators/_helpers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd) 3 | source "$SCRIPT_DIR"/../utilities/shell_logger.sh 4 | 5 | load_dotenv() { 6 | local dotenv_file_path="${1:-".env"}" 7 | 8 | if [[ -f "${dotenv_file_path}" ]]; then 9 | _information "Loading .env file: ${dotenv_file_path}" 10 | set -o allexport 11 | source "${dotenv_file_path}" 12 | set +o allexport 13 | fi 14 | } 15 | -------------------------------------------------------------------------------- /scripts/orchestrators/_setup_helpers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | get_os_architecture() { 4 | local arch_amd64="${1:-"amd64"}" 5 | local arch_arm64="${2:-"arm64"}" 6 | local arch_arm="${3:-"arm"}" 7 | local arch_386="${4:-"386"}" 8 | 9 | os_architecture="$(uname -m)" 10 | case ${os_architecture} in 11 | x86_64) os_architecture="${arch_amd64}" ;; 12 | aarch64 | armv8*) os_architecture="${arch_arm64}" ;; 13 | aarch32 | armv7* | armvhf*) os_architecture="${arch_arm}" ;; 14 | i?86) os_architecture="${arch_386}" ;; 15 | *) 16 | _error "Architecture ${os_architecture} unsupported" 17 | exit 1 18 | ;; 19 | esac 20 | } 21 | 22 | # Figure out correct version of a three part version number is not passed 23 | find_version_from_git_tags() { 24 | local variable_name=$1 25 | local requested_version=${!variable_name} 26 | if [ "${requested_version}" = "none" ]; then return; fi 27 | local repository=$2 28 | local prefix=${3:-"tags/v"} 29 | local separator=${4:-"."} 30 | local last_part_optional=${5:-"false"} 31 | 32 | if [ "${separator}" = "none" ]; then 33 | separator="" 34 | fi 35 | 36 | if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then 37 | local escaped_separator=${separator//./\\.} 38 | local last_part 39 | if [ "${last_part_optional}" = "true" ]; then 40 | last_part="(${escaped_separator}[0-9]+)?" 41 | else 42 | last_part="${escaped_separator}[0-9]+" 43 | fi 44 | local regex="${prefix}\\K[0-9]+${escaped_separator}[0-9]+${last_part}(-[A-Za-z0-9]+)*$" 45 | local version_list="$(git ls-remote --tags "${repository}" | grep -oP "${regex}" | tr -d ' ' | tr "${separator}" "." | sort -rV)" 46 | if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then 47 | declare -g "${variable_name}"="$(echo "${version_list}" | head -n 1)" 48 | else 49 | set +e 50 | declare -g "${variable_name}"="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")" 51 | set -e 52 | fi 53 | fi 54 | if [ -z "${!variable_name}" ] || ! echo "${version_list}" | grep "^${!variable_name//./\\.}$" >/dev/null 2>&1; then 55 | _error "Invalid ${variable_name} value: ${requested_version}\nValid values:\n${version_list}" >&2 56 | exit 1 57 | fi 58 | echo "${variable_name}=${!variable_name}" 59 | } 60 | -------------------------------------------------------------------------------- /scripts/orchestrators/events.sh: -------------------------------------------------------------------------------- 1 | #!/bin/env bash 2 | 3 | # Includes 4 | source ./_helpers.sh 5 | 6 | # expects EVENTS_STORAGE_ACCOUNT, EVENTS_TABLE_NAME to be set 7 | 8 | usage() { 9 | _information "Usage: helpers to store and query events" 10 | exit 1 11 | } 12 | 13 | query_events() { 14 | local pipeline_name=$1 15 | local event_name=$2 16 | local event_group_id=$3 17 | 18 | filter_expression="${odata_filter} PartitionKey eq '${pipeline_name}'" 19 | 20 | cmd="az storage entity query \ 21 | --auth-mode login \ 22 | --account-name ${EVENTS_STORAGE_ACCOUNT} \ 23 | --table-name ${EVENTS_TABLE_NAME} \ 24 | --filter \"\ 25 | PartitionKey eq '${pipeline_name}' and \ 26 | EventName eq '${event_name}' and \ 27 | EventGroupId eq '${event_group_id}'\"" 28 | 29 | resultJson=$(eval "${cmd}") 30 | exit_code=$? 31 | 32 | if [[ ${exit_code} != 0 ]]; then 33 | return ${exit_code} 34 | fi 35 | 36 | local values=$(echo "${resultJson}" | jq -r ".items") 37 | 38 | echo $values 39 | } 40 | 41 | store_event() { 42 | local pipeline_name=$1 43 | local event_name=$2 44 | local event_group_id=$3 45 | local data=$4 46 | 47 | # data is a set of key=value pairs, separated by spaces 48 | 49 | local id=$(uuidgen) 50 | 51 | _information "Store event ${event_name} with group id ${event_group_id} from pipeline ${pipeline_name} with id ${id}" 52 | 53 | local cmd="az storage entity insert \ 54 | --auth-mode login \ 55 | --entity \ 56 | PartitionKey=${pipeline_name} \ 57 | RowKey=$id \ 58 | EventName=${event_name} \ 59 | EventGroupId=${event_group_id} \ 60 | $data \ 61 | --table-name $EVENTS_TABLE_NAME \ 62 | --account-name $EVENTS_STORAGE_ACCOUNT" 63 | 64 | _information "Executing: ${cmd}" 65 | eval "${cmd}" 66 | 67 | return $? 68 | } 69 | -------------------------------------------------------------------------------- /scripts/orchestrators/git.diff.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Includes 4 | source _helpers.sh 5 | 6 | usage() { 7 | _information "Usage: Git helper to report diff between two git branches " 8 | exit 1 9 | } 10 | 11 | # git_diff "main" "Update_iac_terraform" "./../../IAC/Terraform/terraform" return_res_var 12 | git_diff() { 13 | base_branch=$1 14 | new_branch=$2 15 | path=$3 16 | result=$4 17 | 18 | _information "Run Git diff cmd to detect layers changes" 19 | 20 | cmd_options="--diff-filter=d --name-only ${base_branch}..${new_branch} " 21 | 22 | if [[ ! -z "$3" ]]; then 23 | cmd_options="${cmd_options} ${path}" 24 | fi 25 | 26 | echo "git diff $cmd_options | xargs -L1 dirname | uniq" 27 | res=$(git diff "$cmd_options" | xargs -L1 dirname | uniq) 28 | 29 | SAVEIFS=${IFS} 30 | IFS=$'\n' 31 | 32 | array=($res) 33 | IFS=${SAVEIFS} 34 | 35 | len=${#array[@]} 36 | 37 | _information "Changes Detected in ${len} layers" 38 | _information "$res" 39 | 40 | if [[ "$4" ]]; then 41 | eval "$result"="'$res'" 42 | else 43 | echo "$res" 44 | fi 45 | } 46 | -------------------------------------------------------------------------------- /scripts/orchestrators/iac.bicep.destroy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./iac.bicep.sh 4 | 5 | pushd . 6 | 7 | cd "${WORKSPACE_PATH}/IAC/Bicep/bicep" || exit 8 | 9 | SAVEIFS=${IFS} 10 | IFS=$'\n' 11 | modules=($(find . -type f -name 'main.bicep' | sort -u -r)) 12 | IFS=${SAVEIFS} 13 | 14 | for deployment in "${modules[@]}"; do 15 | _information "Executing Bicep destroy: ${deployment}" 16 | 17 | layerName=$(basename "$(dirname "$(dirname "${deployment}")")") 18 | 19 | output=$(destroy "${ENVIRONMENT_NAME}" "${layerName}" "${LOCATION_NAME}") 20 | 21 | exit_code=$? 22 | 23 | if [[ ${exit_code} != 0 ]]; then 24 | _error "Bicep destroy failed - returned code ${exit_code}" 25 | exit ${exit_code} 26 | fi 27 | 28 | echo "------------------------" 29 | done 30 | 31 | popd || exit 32 | -------------------------------------------------------------------------------- /scripts/orchestrators/iac.bicep.lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./iac.bicep.sh 4 | source ./scanners.sh 5 | 6 | pushd . 7 | 8 | looking_path="${WORKSPACE_PATH}/IAC/Bicep/bicep" 9 | 10 | cd "${looking_path}" || exit 11 | 12 | SAVEIFS=${IFS} 13 | IFS=$'\n' 14 | modules=($(find . -type f -name '*.bicep' | sort -u)) 15 | IFS=${SAVEIFS} 16 | 17 | popd || exit 18 | 19 | # LINT 20 | for deployment in "${modules[@]}"; do 21 | SANITIZED_EXCLUDED_FOLDERS=",${EXCLUDED_FOLDERS}," 22 | SANITIZED_EXCLUDED_FOLDERS=${SANITIZED_EXCLUDED_FOLDERS//;/,} 23 | 24 | dirname=$(dirname "${deployment}") 25 | sanitized_dirname=${dirname//.\//} 26 | 27 | if [[ ${sanitized_dirname} == __* ]]; then 28 | _information "Skipping ${deployment}" 29 | echo "" 30 | echo "------------------------" 31 | continue 32 | fi 33 | 34 | if [[ ${SANITIZED_EXCLUDED_FOLDERS} == *",${sanitized_dirname},"* ]]; then 35 | _information "${sanitized_dirname} excluded" 36 | echo "" 37 | echo "------------------------" 38 | continue 39 | fi 40 | 41 | deployment=${deployment/'./'/"${looking_path}/"} 42 | 43 | _information "Executing Bicep lint for: ${deployment}" 44 | 45 | lint "${deployment}" 46 | exit_code=$? 47 | 48 | if [[ ${exit_code} != 0 ]]; then 49 | _error "Bicep lint failed - returned code ${exit_code}" 50 | exit ${exit_code} 51 | fi 52 | 53 | _information "------------------------" 54 | done 55 | 56 | # ARM-TTK 57 | for deployment in "${modules[@]}"; do 58 | deployment=${deployment/'./'/"${looking_path}/"} 59 | _information "Executing ARM-TTK for: ${deployment}" 60 | 61 | run_armttk "${deployment}" 62 | exit_code=$? 63 | 64 | if [[ ${exit_code} != 0 ]]; then 65 | _error "ARM-TTK failed - returned code ${exit_code}" 66 | exit ${exit_code} 67 | fi 68 | 69 | echo "------------------------" 70 | done 71 | -------------------------------------------------------------------------------- /scripts/orchestrators/iac.bicep.previewdeploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./iac.bicep.sh 4 | 5 | pushd . 6 | 7 | # in case ENVIRONMENT_DIRECTORY is empty, we set it to ENVIRONMENT_NAME (for backwards compatibility) 8 | if [[ -z "${ENVIRONMENT_DIRECTORY}" ]]; then 9 | ENVIRONMENT_DIRECTORY="${ENVIRONMENT_NAME}" 10 | fi 11 | 12 | cd "${WORKSPACE_PATH}/IAC/Bicep/bicep" || exit 13 | 14 | SAVEIFS=${IFS} 15 | IFS=$'\n' 16 | modules=($(find . -type f -name 'main.bicep' | sort -u)) 17 | IFS=${SAVEIFS} 18 | 19 | for deployment in "${modules[@]}"; do 20 | SANITIZED_EXCLUDED_FOLDERS=",${EXCLUDED_FOLDERS}," 21 | SANITIZED_EXCLUDED_FOLDERS=${SANITIZED_EXCLUDED_FOLDERS//;/,} 22 | 23 | dirname=$(dirname "${deployment}") 24 | sanitized_dirname=${dirname//.\//} 25 | 26 | if [[ ${sanitized_dirname} == __* ]]; then 27 | _information "Skipping ${deployment}" 28 | echo "" 29 | echo "------------------------" 30 | continue 31 | fi 32 | 33 | if [[ ${SANITIZED_EXCLUDED_FOLDERS} == *",${sanitized_dirname},"* ]]; then 34 | _information "${sanitized_dirname} excluded" 35 | echo "" 36 | echo "------------------------" 37 | continue 38 | fi 39 | 40 | _information "Preparing for ${deployment}" 41 | 42 | path=$(dirname "${deployment}") 43 | export layerName=$(basename "$(dirname "$(dirname "${deployment}")")") 44 | 45 | params=() 46 | SAVEIFS=${IFS} 47 | IFS=$'\n' 48 | param_tmp_deployment="${WORKSPACE_PATH}/env/bicep/${ENVIRONMENT_DIRECTORY}/${path//.\//}/" 49 | if [[ -d "${param_tmp_deployment}" ]]; then 50 | params+=($(find "${param_tmp_deployment}" -maxdepth 1 -type f -name '*parameters*.bicepparam')) 51 | fi 52 | IFS=${SAVEIFS} 53 | 54 | layer_folder_path=$(dirname "${deployment}") 55 | if [ -f "${layer_folder_path}/_events.sh" ]; then 56 | source "${layer_folder_path}/_events.sh" 57 | fi 58 | 59 | if [ "$(type -t pre_deploy)" == "function" ]; then 60 | pre_deploy 61 | fi 62 | 63 | az bicep upgrade 64 | az config set bicep.check_version=False 65 | 66 | load_dotenv 67 | 68 | # resourceGroupName is a bicep output that is stored in an environment variable. 69 | _information "Executing Bicep preview: 'preview \"${deployment}\" params_path \"${RUN_ID}\" \"${LOCATION_NAME}\" \"${resourceGroupName}\"'" 70 | 71 | output=$(preview "${deployment}" "${params[0]}" "${RUN_ID}" "${LOCATION_NAME}" "${resourceGroupName}") 72 | 73 | exit_code=$? 74 | 75 | if [[ ${exit_code} != 0 ]]; then 76 | _error "Bicep preview failed - returned code ${exit_code}" 77 | exit ${exit_code} 78 | fi 79 | 80 | _information "Executing Bicep deploy: 'deploy \"${deployment}\" params_path \"${RUN_ID}\" \"${LOCATION_NAME}\" \"${resourceGroupName}\"'" 81 | 82 | output=$(deploy "${deployment}" "${params[0]}" "${RUN_ID}" "${LOCATION_NAME}" "${resourceGroupName}") 83 | 84 | exit_code=$? 85 | 86 | if [[ ${exit_code} != 0 ]]; then 87 | _error "Bicep deploy failed - returned code ${exit_code}" 88 | exit ${exit_code} 89 | fi 90 | if [ "$(type -t post_deploy)" == "function" ]; then 91 | post_deploy 92 | fi 93 | unset -f pre_deploy 94 | unset -f post_deploy 95 | 96 | bicep_output_to_env "${output}" ".env" "false" "true" 97 | done 98 | 99 | popd || exit 100 | -------------------------------------------------------------------------------- /scripts/orchestrators/iac.bicep.validate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./iac.bicep.sh 4 | source ../utilities/os.sh 5 | 6 | pushd . 7 | 8 | # in case ENVIRONMENT_DIRECTORY is empty, we set it to ENVIRONMENT_NAME (for backwards compatibility) 9 | if [[ -z "${ENVIRONMENT_DIRECTORY}" ]]; then 10 | ENVIRONMENT_DIRECTORY="${ENVIRONMENT_NAME}" 11 | fi 12 | 13 | cd "${WORKSPACE_PATH}/IAC/Bicep/bicep" 14 | 15 | SAVEIFS=${IFS} 16 | IFS=$'\n' 17 | modules=($(find . -type f -name 'main.bicep' | sort -u)) 18 | IFS=${SAVEIFS} 19 | 20 | for deployment in "${modules[@]}"; do 21 | SANITIZED_EXCLUDED_FOLDERS=",${EXCLUDED_FOLDERS}," 22 | SANITIZED_EXCLUDED_FOLDERS=${SANITIZED_EXCLUDED_FOLDERS//;/,} 23 | 24 | dirname=$(dirname "${deployment}") 25 | sanitized_dirname=${dirname//.\//} 26 | 27 | if [[ ${sanitized_dirname} == __* ]]; then 28 | _information "Skipping ${deployment}" 29 | echo "" 30 | echo "------------------------" 31 | continue 32 | fi 33 | 34 | if [[ ${SANITIZED_EXCLUDED_FOLDERS} == *",${sanitized_dirname},"* ]]; then 35 | _information "${sanitized_dirname} excluded" 36 | echo "" 37 | echo "------------------------" 38 | continue 39 | fi 40 | 41 | _information "Executing Bicep validate: ${deployment}" 42 | 43 | path=$(dirname "${deployment}") 44 | export layerName=$(basename "$(dirname "$(dirname "${deployment}")")") 45 | 46 | params=() 47 | SAVEIFS=${IFS} 48 | IFS=$'\n' 49 | param_tmp_deployment="${WORKSPACE_PATH}/env/bicep/${ENVIRONMENT_DIRECTORY}/${path//.\//}/" 50 | if [[ -d "${param_tmp_deployment}" ]]; then 51 | params+=($(find "${param_tmp_deployment}" -maxdepth 1 -type f -name '*parameters*.bicepparam')) 52 | fi 53 | IFS=${SAVEIFS} 54 | 55 | layer_folder_path=$(dirname "${deployment}") 56 | if [ -f "${layer_folder_path}/_events.sh" ]; then 57 | source "${layer_folder_path}/_events.sh" 58 | fi 59 | 60 | if [ "$(type -t pre_validate)" == "function" ]; then 61 | pre_validate 62 | fi 63 | 64 | az bicep upgrade 65 | az config set bicep.check_version=False 66 | 67 | load_dotenv 68 | 69 | uniquer=$(echo $RANDOM | md5sum | head -c 6) 70 | output=$(validate "${deployment}" "${params[0]}" "${RUN_ID}" "${LOCATION_NAME}" "rg${uniquer}validate" "${layerName}") 71 | exit_code=$? 72 | 73 | if [[ ${exit_code} != 0 ]]; then 74 | _error "Bicep validate failed - returned code ${exit_code}" 75 | exit ${exit_code} 76 | fi 77 | 78 | if [ "$(type -t post_validate)" == "function" ]; then 79 | post_validate 80 | fi 81 | unset -f pre_validate 82 | unset -f post_validate 83 | 84 | bicep_output_to_env "${output}" 85 | 86 | echo "------------------------" 87 | done 88 | 89 | popd 90 | -------------------------------------------------------------------------------- /scripts/orchestrators/iac.tf.destroy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./iac.tf.sh 4 | 5 | pushd "${WORKSPACE_PATH}/IAC/Terraform/terraform" || exit 6 | 7 | SAVEIFS=${IFS} 8 | IFS=$'\n' 9 | modules=($(find . -type f -name main.tf | sort -r | awk '$0 !~ last "/" {print last} {last=$0} END {print last}')) 10 | IFS=${SAVEIFS} 11 | 12 | # in case ENVIRONMENT_DIRECTORY is empty, we set it to ENVIRONMENT_NAME (for backwards compatibility) 13 | if [[ -z "${ENVIRONMENT_DIRECTORY}" ]]; then 14 | ENVIRONMENT_DIRECTORY="${ENVIRONMENT_NAME}" 15 | fi 16 | 17 | export TF_VAR_env=$ENVIRONMENT_NAME 18 | 19 | for deployment in "${modules[@]}"; do 20 | if [[ ${deployment} != *"01_init"* ]]; then 21 | tfPath=$(dirname "${deployment}") 22 | 23 | _information "Executing tf destroy: ${tfPath}" 24 | pushd "${tfPath}" || exit 25 | 26 | init true "${ENVIRONMENT_NAME}${tfPath}.tfstate" "${STATE_STORAGE_ACCOUNT}" "${STATE_CONTAINER}" "${STATE_RG}" 27 | 28 | envfile=${tfPath/'./'/''} 29 | envfile=${envfile/'/'/'_'} 30 | 31 | destroy "${WORKSPACE_PATH}/env/terraform/${ENVIRONMENT_DIRECTORY}/${envfile}.tfvars.json" 32 | exit_code=$? 33 | 34 | if [[ ${exit_code} != 0 ]]; then 35 | _error "tf destroy failed - returned code ${exit_code}" 36 | exit ${exit_code} 37 | fi 38 | popd || exit 39 | fi 40 | done 41 | popd || exit 42 | -------------------------------------------------------------------------------- /scripts/orchestrators/iac.tf.lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo 'Run tflint' 4 | source ./iac.tf.sh 5 | pushd "${WORKSPACE_PATH}/IAC/Terraform/terraform" 6 | 7 | SAVEIFS=${IFS} 8 | IFS=$'\n' 9 | modules=($(find . -type d | sort | awk '$0 !~ last "/" {print last} {last=$0} END {print last}')) 10 | SAVEIFS=${IFS} 11 | 12 | for deployment in "${modules[@]}"; do 13 | _information "Executing tf lint for: ${deployment}" 14 | pushd ${deployment} 15 | lint 16 | code=$? 17 | if [[ $code != 0 ]]; then 18 | _error "tflint failed- returned code ${code}" 19 | exit ${code} 20 | fi 21 | popd 22 | 23 | done 24 | popd 25 | -------------------------------------------------------------------------------- /scripts/orchestrators/iac.tf.previewdeploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./iac.tf.sh 4 | pushd "${WORKSPACE_PATH}"/IAC/Terraform/terraform || exit 5 | modules=$(find . -type d | sort | awk '$0 !~ last "/" {print last} {last=$0} END {print last}') 6 | 7 | SAVEIFS=$IFS 8 | IFS=$'\n' 9 | array=($modules) 10 | IFS=$SAVEIFS 11 | len=${#array[@]} 12 | 13 | # in case ENVIRONMENT_DIRECTORY is empty, we set it to ENVIRONMENT_NAME (for backwards compatibility) 14 | if [[ -z "${ENVIRONMENT_DIRECTORY}" ]]; then 15 | ENVIRONMENT_DIRECTORY="${ENVIRONMENT_NAME}" 16 | fi 17 | 18 | for deployment in "${array[@]}"; do 19 | if [[ ${deployment} != *"01_init"* ]]; then 20 | echo "tf init ${deployment}" 21 | pushd "$deployment" || exit 22 | init true "${ENVIRONMENT_NAME}/${deployment/'./'/''}.tfstate" "${STATE_STORAGE_ACCOUNT}" "${STATE_CONTAINER}" "${STATE_RG}" 23 | # Preview deployment 24 | envfile=${deployment/'./'/''} 25 | envfile=${envfile/'/'/'_'} 26 | 27 | layer_folder_path=$(dirname "${deployment}") 28 | if [ -f "${layer_folder_path}/_events.sh" ]; then 29 | source "${layer_folder_path}/_events.sh" 30 | fi 31 | 32 | if [ "$(type -t pre_deploy)" == "function" ]; then 33 | pre_deploy 34 | fi 35 | 36 | preview "terraform.tfplan" "${WORKSPACE_PATH}/env/terraform/${ENVIRONMENT_DIRECTORY}/${envfile}.tfvars.json" 37 | code=$? 38 | 39 | if [[ $code != 0 ]]; then 40 | echo "terraform plan - returned code ${code}" 41 | exit $code 42 | fi 43 | 44 | # Check for resources destruction 45 | detect_destroy "terraform.tfplan" 46 | # Apply deployment 47 | deploy "terraform.tfplan" 48 | code=$? 49 | if [[ $code != 0 ]]; then 50 | echo "terraform apply - returned code ${code}" 51 | exit $code 52 | fi 53 | 54 | if [ "$(type -t post_deploy)" == "function" ]; then 55 | post_deploy 56 | fi 57 | unset -f pre_deploy 58 | unset -f post_deploy 59 | 60 | popd || exit 61 | 62 | fi 63 | done 64 | popd || exit 65 | -------------------------------------------------------------------------------- /scripts/orchestrators/iac.tf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Includes 5 | source _helpers.sh 6 | 7 | environment="__ENVIRONMENT__" 8 | subscription_id="__SUBSCRIPTION_ID__" 9 | tenant_id="__TENANT_ID__" 10 | layers="__LAYERS__" 11 | deployments="__DEPLOYMENTS__" 12 | 13 | usage() { 14 | _information "Usage: IAC terraform commands helper" 15 | exit 1 16 | } 17 | 18 | set_arm_env_vars() { 19 | # retrieve client_id, subscription_id, tenant_id from logged in user 20 | azaccount=$(az account show) 21 | subscription_id=$(echo $azaccount | jq -r .id) 22 | 23 | export ARM_SUBSCRIPTION_ID=$subscription_id 24 | export ARM_USE_AZUREAD=true 25 | export ARM_STORAGE_USE_AZUREAD=true 26 | } 27 | 28 | init() { 29 | backend_config=$1 30 | key=$2 31 | storage_account_name=$3 32 | container_name=$4 33 | resource_group_name=$5 34 | 35 | set_arm_env_vars 36 | 37 | if [ "${backend_config}" == "false" ]; then 38 | _information "Execute terraform init" 39 | terraform init 40 | else 41 | _information "Execute terraform init with backend-config" 42 | 43 | echo "terraform init \ 44 | -backend-config=storage_account_name=${storage_account_name} \ 45 | -backend-config=container_name=${container_name} \ 46 | -backend-config=key=${key} \ 47 | -backend-config=resource_group_name=${resource_group_name} \ 48 | -reconfigure" 49 | 50 | terraform init \ 51 | -backend-config=storage_account_name=${storage_account_name} \ 52 | -backend-config=container_name=${container_name} \ 53 | -backend-config=key=${key} \ 54 | -backend-config=resource_group_name=${resource_group_name} \ 55 | -reconfigure 56 | fi 57 | } 58 | 59 | format() { 60 | _information "Execute terraform fmt" 61 | terraform fmt 62 | exit $? 63 | } 64 | 65 | validate() { 66 | _information "Execute terraform validate" 67 | terraform validate 68 | return $? 69 | } 70 | 71 | _warning() { 72 | echo "WARNING: $1" 73 | } 74 | 75 | preview() { 76 | plan_file_name=$1 77 | var_file=$2 78 | 79 | set_arm_env_vars 80 | 81 | _information "Execute terraform plan" 82 | if [[ -z "$2" ]]; then 83 | echo "terraform plan -input=false -out=${plan_file_name}" 84 | terraform plan -input=false -out=${plan_file_name} 85 | else 86 | echo "terraform plan -input=false -out=${plan_file_name} -var-file=${var_file}" 87 | terraform plan -input=false -out=${plan_file_name} -var-file=${var_file} 88 | fi 89 | 90 | return $? 91 | } 92 | 93 | deploy() { 94 | plan_file_name=$1 95 | 96 | set_arm_env_vars 97 | 98 | _information "Execute terraform apply" 99 | echo "terraform apply -input=false -auto-approve ${plan_file_name}" 100 | terraform apply -input=false -auto-approve ${plan_file_name} 101 | 102 | exit_code=$? 103 | 104 | return $exit_code 105 | } 106 | 107 | destroy() { 108 | var_file=$1 109 | 110 | set_arm_env_vars 111 | 112 | _information "Execute terraform destroy" 113 | terraform destroy -input=false -auto-approve -var-file=${var_file} 114 | return $? 115 | } 116 | 117 | detect_destroy() { 118 | plan_file_name=$1 119 | _information "Detect destroy in .tfplan file" 120 | 121 | terraform show -no-color -json ${plan_file_name} >mytmp.json 122 | actions=$(jq $filePath 2>&1 140 | 141 | local code=$? 142 | if [[ -z $(grep '[^[:space:]]' $filePath) ]]; then 143 | echo "tflint passed" 144 | #exit 0 145 | else 146 | echo "tflint failed. lint results in file name ${lint_res_file_name}" 147 | sed -i 's/\x1b\[[0-9;]*m//g' $filePath 148 | cat $filePath 149 | #exit 1 150 | fi 151 | return $code 152 | } 153 | -------------------------------------------------------------------------------- /scripts/orchestrators/iac.tf.statebackup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./iac.tf.sh 4 | 5 | echo "Starting backup of ${ENVIRONMENT_NAME} environment remote state. Commit: ${COMMIT_ID}" 6 | 7 | # Create backup container name and convert to lower case 8 | backupContainerName=$(echo ${RUN_ID}-${COMMIT_ID} | tr '[:upper:]' '[:lower:]') 9 | backupResourceGroup=$(az storage account list --query "[?name=='$STATE_STORAGE_ACCOUNT_BACKUP'].resourceGroup" -o tsv) 10 | 11 | sourceEndPoint=$(az storage account list -g "${STATE_RG}" --query "[?name=='$STATE_STORAGE_ACCOUNT'].{endpointName:primaryEndpoints.blob}" -o tsv) 12 | backupEndpoint=$(az storage account list -g $backupResourceGroup --query "[?name=='$STATE_STORAGE_ACCOUNT_BACKUP'].{endpointName:primaryEndpoints.blob}" -o tsv) 13 | 14 | echo "Copying remote state to container ${backupContainerName} in storage account $STATE_STORAGE_ACCOUNT_BACKUP located in resource group ${backupResourceGroup}" 15 | 16 | # Checking if azcopy is installed. Otherwise, install it manually because the version that the az cli installs 17 | # has some issues when trying to use the authentication with the az cli 18 | 19 | azCopyFilePath=$AZURE_CONFIG_DIR/bin/azcopy 20 | if [ ! -f "$azCopyFilePath" ]; then 21 | echo "azcopy not found in $azCopyFilePath. Downloading..." 22 | curl -L https://aka.ms/downloadazcopy-v10-linux -o azcopy_linux_amd64.tar.gz 23 | tar -xf azcopy_linux_amd64.tar.gz 24 | 25 | echo "Copying azcopy to $azCopyFilePath" 26 | mkdir -p $AZURE_CONFIG_DIR/bin/ 27 | cp azcopy_linux_amd64*/azcopy $azCopyFilePath 28 | fi 29 | 30 | 31 | az storage copy --auth-mode login -s ${sourceEndPoint}${STATE_CONTAINER}/${ENVIRONMENT_NAME}/* -d ${backupEndpoint}${STATE_CONTAINER}/${ENVIRONMENT_NAME}/${backupContainerName} --recursive 32 | -------------------------------------------------------------------------------- /scripts/orchestrators/iac.tf.test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./tests.runner.sh 4 | pushd "${WORKSPACE_PATH}/IAC/Terraform/test/terraform" 5 | 6 | # install junit 7 | echo "install go-junit-report" 8 | go install github.com/jstemmer/go-junit-report@latest 9 | 10 | # set test vars 11 | export resource_group_name="${STATE_RG}" 12 | export storage_account_name="${STATE_STORAGE_ACCOUNT}" 13 | export container_name="${STATE_CONTAINER}" 14 | 15 | # retrieve client_id, subscription_id, tenant_id from logged in user 16 | azaccount=$(az account show) 17 | subscription_id=$(echo $azaccount | jq -r .id) 18 | 19 | export ARM_SUBSCRIPTION_ID=$subscription_id 20 | export ARM_USE_AZUREAD=true 21 | export ARM_STORAGE_USE_AZUREAD=true 22 | 23 | if [[ "${TEST_TAG}" == "module_tests" ]]; then 24 | echo "Run tests with tag = module_tests" 25 | terraform module_test true 26 | elif [[ "${TEST_TAG}" == "e2e_test" ]]; then 27 | echo "Run tests with tag = e2e_test" 28 | terraform e2e_test true 29 | else 30 | SAVEIFS=${IFS} 31 | IFS=$'\n' 32 | tests=($(find . -type f -name '*end_test.go' -print)) 33 | IFS=${SAVEIFS} 34 | 35 | for test in "${tests[@]}"; do 36 | terraform ${test/'./'/''} 37 | done 38 | fi 39 | 40 | popd 41 | -------------------------------------------------------------------------------- /scripts/orchestrators/iac.tf.validate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./iac.tf.sh 4 | pushd "${WORKSPACE_PATH}/IAC/Terraform/terraform" 5 | modules=$(find . -type d | sort | awk '$0 !~ last "/" {print last} {last=$0} END {print last}') 6 | 7 | SAVEIFS=$IFS 8 | IFS=$'\n' 9 | array=($modules) 10 | IFS=$SAVEIFS 11 | len=${#array[@]} 12 | 13 | for deployment in "${array[@]}"; do 14 | if [[ ${deployment} != *"01_init"* ]]; then 15 | echo "tf init ${deployment}" 16 | pushd $deployment 17 | 18 | init true "${ENVIRONMENT_NAME}${deployment}.tfstate" "${STATE_STORAGE_ACCOUNT}" "${STATE_CONTAINER}" "${STATE_RG}" 19 | echo "tf init ${deployment}" 20 | 21 | layer_folder_path=$(dirname "${deployment}") 22 | if [ -f "${layer_folder_path}/_events.sh" ]; then 23 | source "${layer_folder_path}/_events.sh" 24 | fi 25 | 26 | if [ "$(type -t pre_validate)" == "function" ]; then 27 | pre_validate 28 | fi 29 | 30 | echo "tf validate ${deployment}" 31 | validate 32 | code=$? 33 | if [[ $code != 0 ]]; then 34 | echo "terraform validate - returned code ${code}" 35 | exit $code 36 | fi 37 | if [ "$(type -t post_validate)" == "function" ]; then 38 | post_validate 39 | fi 40 | unset -f pre_validate 41 | unset -f post_validate 42 | 43 | popd 44 | 45 | fi 46 | done 47 | popd 48 | -------------------------------------------------------------------------------- /scripts/orchestrators/scanners.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Includes 4 | source ./_helpers.sh 5 | 6 | usage() { 7 | _information "Usage: scanners.sh -place holder for all scanners/security tools execution" 8 | exit 1 9 | } 10 | 11 | run_gitleaks() { 12 | local source_path=$1 13 | local report_format=$2 14 | local log_level=$3 15 | local redact=$4 16 | local verbose=$5 17 | local no_git=$6 18 | 19 | _information "Run Gitleaks detect cmd" 20 | 21 | cmd_options="--config ${source_path}/.gitleaks.toml --source ${source_path} --report-path ./gitleaks-report.${report_format} --report-format ${report_format} --log-level ${log_level}" 22 | 23 | if [[ ! -z "${redact}" ]]; then 24 | cmd_options+=" --redact" 25 | fi 26 | 27 | if [[ ! -z "${verbose}" ]]; then 28 | cmd_options+=" --verbose" 29 | fi 30 | 31 | if [[ ! -z "${no_git}" ]]; then 32 | cmd_options+=" --no-git" 33 | fi 34 | 35 | echo "gitleaks detect ${cmd_options}" 36 | gitleaks detect ${cmd_options} 37 | exit $? 38 | } 39 | # export -f run_gitleaks 40 | 41 | run_armttk() { 42 | local bicep_file_path=$1 43 | 44 | az bicep build --file "${bicep_file_path}" 45 | arm-ttk/arm-ttk/Test-AzTemplate.sh "${bicep_file_path/.bicep/.json}" 46 | } 47 | # export -f run_armttk 48 | -------------------------------------------------------------------------------- /scripts/orchestrators/setup-age.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Syntax: ./setup-age.sh [version] 4 | 5 | # check if age is already installed 6 | if [ -f "/usr/local/bin/age" ]; then 7 | echo "age is already installed" 8 | exit 0 9 | fi 10 | 11 | # Includes 12 | source _helpers.sh 13 | source _setup_helpers.sh 14 | 15 | set -e 16 | VERSION="${1:-"latest"}" 17 | 18 | # Get OS architecture 19 | get_os_architecture 20 | 21 | # Verify requested version is available, convert latest 22 | find_version_from_git_tags VERSION 'https://github.com/FiloSottile/age' 23 | 24 | _information "Downloading Age..." 25 | 26 | filename="age-v${VERSION}-linux-${os_architecture}.tar.gz" 27 | curl -sSL -o "${filename}" "https://github.com/FiloSottile/age/releases/download/v${VERSION}/${filename}" 28 | tar -xf "${filename}" age 29 | rm -f "${filename}" 30 | sudo mv -f age/age /usr/local/bin/ 31 | sudo mv -f age/age-keygen /usr/local/bin/ 32 | rm -rf age 33 | -------------------------------------------------------------------------------- /scripts/orchestrators/setup-armttk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Syntax: ./setup-armttk.sh [version] 4 | 5 | # check if armttk is already installed 6 | if [ -f "./arm-ttk/Test-AzTemplate.cmd" ]; then 7 | echo "armttk is already installed" 8 | exit 0 9 | fi 10 | 11 | # Includes 12 | source _helpers.sh 13 | source _setup_helpers.sh 14 | 15 | set -e 16 | VERSION="${1:-"20230619"}" 17 | 18 | # Verify requested version is available, convert latest 19 | find_version_from_git_tags VERSION 'https://github.com/Azure/arm-ttk' 'tags/' 'none' 20 | 21 | _information "Downloading ARM-TTK..." 22 | filename="arm-ttk.zip" 23 | curl -sSL -o "${filename}" "https://github.com/Azure/arm-ttk/releases/download/${VERSION}/${filename}" 24 | unzip "${filename}" 25 | rm -f "${filename}" 26 | 27 | LOCAL_READLINK=readlink 28 | 29 | # https://stackoverflow.com/questions/3466166/how-to-check-if-running-in-cygwin-mac-or-linux 30 | unameOut="$(uname -s)" 31 | case "${unameOut}" in 32 | Darwin*) LOCAL_READLINK=greadlink ;; 33 | esac 34 | 35 | ARMTTK_PATH="$(dirname $(${LOCAL_READLINK} -f $0))/arm-ttk/arm-ttk" 36 | 37 | pwsh -noprofile -nologo -command "Import-Module '${ARMTTK_PATH}/arm-ttk.psd1'" 38 | 39 | chmod +x "${ARMTTK_PATH}/Test-AzTemplate.sh" 40 | 41 | echo "PATH=${PATH:+${PATH}:}${ARMTTK_PATH}" >>~/.bashrc 42 | -------------------------------------------------------------------------------- /scripts/orchestrators/setup-azcli.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Syntax: ./setup-azcli.sh 4 | 5 | # If it's running as a Github action, Create a temporary directory 6 | # for Azure CLI configuration isolation 7 | 8 | if [ "$GITHUB_ACTIONS" == "true" ]; then 9 | temp_dir=$(mktemp -d) 10 | export AZURE_CONFIG_DIR="$temp_dir" 11 | echo "AZURE_CONFIG_DIR=$AZURE_CONFIG_DIR" >> $GITHUB_ENV 12 | fi 13 | 14 | # check if azurecli is already installed 15 | if command -v az &>/dev/null; then 16 | echo "Azure CLI is already installed" 17 | exit 0 18 | fi 19 | 20 | # Includes 21 | source _helpers.sh 22 | source _setup_helpers.sh 23 | 24 | set -e 25 | 26 | _information "Installing Azure CLI..." 27 | 28 | curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash 29 | -------------------------------------------------------------------------------- /scripts/orchestrators/setup-azpowershell.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Syntax: ./setup-pester.sh 4 | 5 | # check if azure powershell is already installed 6 | AZ_MODULE_PATH=$(pwsh -c "Get-Module -ListAvailable -Name Az | Select-Object -ExpandProperty Path") 7 | if [ -n "${AZ_MODULE_PATH}" ]; then 8 | echo "Azure Powershell is already installed" 9 | exit 0 10 | fi 11 | 12 | # Includes 13 | source _helpers.sh 14 | source _setup_helpers.sh 15 | 16 | _information "Downloading Azure Powershell Module..." 17 | 18 | pwsh -noprofile -nologo -command 'Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force' 19 | -------------------------------------------------------------------------------- /scripts/orchestrators/setup-benchpress.ps1: -------------------------------------------------------------------------------- 1 | $AZ_MODULE_PATH=$(Get-Module -ListAvailable -Name Az | Select-Object -ExpandProperty Path) 2 | if (-not $AZ_MODULE_PATH) { 3 | Install-Module -Name Az -AllowClobber -Force -Confirm:$False -SkipPublisherCheck 4 | } 5 | 6 | $AZ_APP_MODULE_PATH=$(Get-Module -ListAvailable -Name Az.App | Select-Object -ExpandProperty Path) 7 | if (-not $AZ_APP_MODULE_PATH) { 8 | Install-Module -Name Az.App -AllowClobber -Force -Confirm:$False -SkipPublisherCheck 9 | } 10 | 11 | $AZ_PORTAL_MODULE_PATH=$(Get-Module -ListAvailable -Name Az.Portal | Select-Object -ExpandProperty Path) 12 | if (-not $AZ_PORTAL_MODULE_PATH) { 13 | Install-Module -Name Az.Portal -AllowClobber -Force -Confirm:$False -SkipPublisherCheck 14 | } 15 | 16 | $BENCHPRESS_AZURE_MODULE_PATH=$(Get-Module -ListAvailable -Name BenchPress.Azure | Select-Object -ExpandProperty Path) 17 | if (-not $BENCHPRESS_AZURE_MODULE_PATH) { 18 | Install-Module -Name BenchPress.Azure -AllowClobber -Force -Confirm:$False -SkipPublisherCheck 19 | } 20 | -------------------------------------------------------------------------------- /scripts/orchestrators/setup-benchpress.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Syntax: ./setup-benchpress.sh 4 | 5 | pwsh -noprofile -nologo -command "./setup-benchpress.ps1" 6 | -------------------------------------------------------------------------------- /scripts/orchestrators/setup-bicep.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Syntax: ./setup-bicep.sh [version] 4 | 5 | # check if bicep is already installed 6 | if [ -f "/usr/local/bin/bicep" ]; then 7 | echo "Bicep is already installed" 8 | exit 0 9 | fi 10 | 11 | # Includes 12 | source _helpers.sh 13 | source _setup_helpers.sh 14 | 15 | set -e 16 | VERSION="${1:-"latest"}" 17 | 18 | # Get OS architecture 19 | get_os_architecture "x64" 20 | 21 | # Verify requested version is available, convert latest 22 | find_version_from_git_tags VERSION 'https://github.com/Azure/bicep' 23 | 24 | _information "Downloading Bicep..." 25 | filename="bicep" 26 | 27 | curl -sSL -o "${filename}" "https://github.com/Azure/bicep/releases/download/v${VERSION}/bicep-linux-${os_architecture}" 28 | chmod +x "./${filename}" 29 | sudo mv "./${filename}" "/usr/local/bin/${filename}" 30 | -------------------------------------------------------------------------------- /scripts/orchestrators/setup-gitleaks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Syntax: ./setup-gitleaks.sh [version] 4 | 5 | # check if gitleaks is already installed 6 | if [ -f "/usr/local/bin/gitleaks" ]; then 7 | echo "gitleaks is already installed" 8 | exit 0 9 | fi 10 | 11 | # Includes 12 | source _helpers.sh 13 | source _setup_helpers.sh 14 | 15 | set -e 16 | VERSION="${1:-"8.21.2"}" 17 | 18 | # Get OS architecture 19 | get_os_architecture "x64" "arm64" "armv7" "x32" 20 | 21 | # Verify requested version is available, convert latest 22 | find_version_from_git_tags VERSION 'https://github.com/gitleaks/gitleaks' 23 | 24 | _information "Downloading Gitleaks..." 25 | 26 | filename="gitleaks_${VERSION}_linux_${os_architecture}.tar.gz" 27 | curl -sSL -o "${filename}" "https://github.com/gitleaks/gitleaks/releases/download/v${VERSION}/${filename}" 28 | tar -xf "${filename}" gitleaks 29 | rm -f "${filename}" 30 | sudo mv -f gitleaks /usr/local/bin/ 31 | -------------------------------------------------------------------------------- /scripts/orchestrators/setup-pester.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Syntax: ./setup-pester.sh 4 | 5 | # check if pester is already installed 6 | PESTER_MODULE_PATH=$(pwsh -c "Get-Module -ListAvailable -Name Pester | Select-Object -ExpandProperty Path") 7 | if [ -n "${PESTER_MODULE_PATH}" ]; then 8 | echo "Pester is already installed" 9 | exit 0 10 | fi 11 | 12 | # Includes 13 | source _helpers.sh 14 | source _setup_helpers.sh 15 | 16 | _information "Downloading Pester..." 17 | 18 | pwsh -noprofile -nologo -command 'Install-Module -Name Pester -AllowClobber -Force -Confirm:$False -SkipPublisherCheck; Import-Module Pester' 19 | -------------------------------------------------------------------------------- /scripts/orchestrators/setup-powershell.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Syntax: ./setup-powershell.sh [version] 4 | 5 | # check if powershell is already installed 6 | 7 | if which pwsh &>/dev/null; then 8 | echo "PowerShell is already installed" 9 | exit 0 10 | fi 11 | 12 | # Includes 13 | source _helpers.sh 14 | source _setup_helpers.sh 15 | 16 | set -e 17 | 18 | _information "Installing PowerShell..." 19 | 20 | sudo apt install -y wget apt-transport-https software-properties-common 21 | wget -q https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb 22 | sudo dpkg -i packages-microsoft-prod.deb 23 | sudo apt update 24 | 25 | sudo apt install -y powershell 26 | -------------------------------------------------------------------------------- /scripts/orchestrators/setup-sops.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Syntax: ./setup-sops.sh [version] 4 | 5 | # check if sops is already installed 6 | if [ -f "/usr/local/bin/sops" ]; then 7 | echo "sops is already installed" 8 | exit 0 9 | fi 10 | 11 | # Includes 12 | source _helpers.sh 13 | source _setup_helpers.sh 14 | 15 | set -e 16 | VERSION="${1:-"latest"}" 17 | 18 | # Get OS architecture 19 | get_os_architecture "amd64" "arm64" 20 | 21 | # Verify requested version is available, convert latest 22 | find_version_from_git_tags VERSION 'https://github.com/mozilla/sops' 23 | 24 | _information "Downloading SOPS..." 25 | 26 | filename="sops-v${VERSION}.linux.${os_architecture}" 27 | echo "${filename}" 28 | curl -sSL -o "${filename}" "https://github.com/mozilla/sops/releases/download/v${VERSION}/${filename}" 29 | sudo mv -f "${filename}" /usr/local/bin/sops 30 | -------------------------------------------------------------------------------- /scripts/orchestrators/setup-terraform.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Syntax: ./setup-terraform.sh [version] 4 | 5 | # Includes 6 | source _helpers.sh 7 | source _setup_helpers.sh 8 | 9 | set -e 10 | VERSION="${1:-"latest"}" 11 | 12 | # Get OS architecture 13 | get_os_architecture 14 | 15 | # Verify requested version is available, convert latest 16 | find_version_from_git_tags VERSION 'https://github.com/hashicorp/terraform' 17 | 18 | _information "Downloading Terraform..." 19 | 20 | filename="terraform_${VERSION}_linux_${os_architecture}.zip" 21 | curl -sSL -o "${filename}" "https://releases.hashicorp.com/terraform/${VERSION}/${filename}" 22 | unzip "${filename}" 23 | rm -f "${filename}" 24 | # ln -s "terraform" /usr/local/bin/terraform 25 | sudo mv -f terraform /usr/local/bin/ 26 | -------------------------------------------------------------------------------- /scripts/orchestrators/setup-tflint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Syntax: ./setup-tflint.sh [version] 4 | 5 | # check if tflint is already installed 6 | if [ -f "/usr/local/bin/tflint" ]; then 7 | echo "tflint is already installed" 8 | exit 0 9 | fi 10 | 11 | # Includes 12 | source _helpers.sh 13 | source _setup_helpers.sh 14 | 15 | set -e 16 | VERSION="${1:-"latest"}" 17 | 18 | # Get OS architecture 19 | get_os_architecture 20 | 21 | # Verify requested version is available, convert latest 22 | find_version_from_git_tags VERSION 'https://github.com/terraform-linters/tflint' 23 | 24 | _information "Downloading Tflint v${VERSION} ..." 25 | 26 | filename="tflint_linux_${os_architecture}.zip" 27 | curl -sSL -o "${filename}" "https://github.com/terraform-linters/tflint/releases/download/v${VERSION}/${filename}" 28 | unzip "${filename}" 29 | rm -f "${filename}" 30 | sudo mv -f tflint /usr/local/bin/ 31 | -------------------------------------------------------------------------------- /scripts/orchestrators/tests.runner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # include helpers 4 | source _helpers.sh 5 | 6 | usage() { 7 | cat <: source ${0} && terraform 26 | # @usage ; source ${0} && terraform 00_dummy_test.go 27 | terraform() { 28 | TEST_FILE_NAME=${1:-} 29 | IS_TAG=${2:-} 30 | 31 | # cd into the terraform directory 32 | pushd ../../terraform/ 33 | 34 | # cleanup any existing state 35 | rm -rf ./terraform/**/.terraform 36 | rm -rf ./terraform/**/*.tfplan 37 | rm -rf ./terraform/**/*.tfstate 38 | rm -rf ./terraform/backend.tfvars 39 | rm -rf ./terraform/**/terraform.tfstate.backup 40 | 41 | # cd to the test directory 42 | cd ../test/terraform 43 | 44 | CWD=$(pwd) 45 | 46 | if [[ -z "${TEST_FILE_NAME}" && -z "${IS_TAG}" ]]; then 47 | # find all tests 48 | TEST_FILE_NAMES=$(find ${CWD}/*.go) 49 | 50 | # run all tests 51 | for TEST_FILE_NAME in ${TEST_FILE_NAMES}; do 52 | echo -e "--------------------------------------------------------------------------------\n[$(date)] : Running tests for '${TEST_FILE_NAME}'" | tee -a test.out 53 | 54 | go test -v -timeout 6000s ${TEST_FILE_NAME} | tee -a test.out 55 | done 56 | elif [ ! -z "${TEST_FILE_NAME}" ] && [ -z "${IS_TAG}" ]; then 57 | echo -e "--------------------------------------------------------------------------------\n[$(date)] : Running tests for '${TEST_FILE_NAME}'" | tee -a test.out 58 | 59 | # run a specific test 60 | echo "go test -v ${TEST_FILE_NAME} 2>&1 | go-junit-report > ${TEST_FILE_NAME/'.go'/'.xml'}" 61 | go test -v -timeout 8000s ${TEST_FILE_NAME} 2>&1 | go-junit-report >${TEST_FILE_NAME/'.go'/'.xml'} 62 | 63 | #gotestsum --junitfile unit-tests.xml -- -tags=moule_test .\... 64 | else 65 | 66 | echo -e "--------------------------------------------------------------------------------\n[$(date)] : Running tests for tag '${TEST_FILE_NAME}'" | tee -a test.out 67 | 68 | # run tests of certain tag 69 | echo "go test -v -timeout 1000s --tags=${TEST_FILE_NAME} 2>&1 | go-junit-report > ${TEST_FILE_NAME}.xml" 70 | go test -v -timeout 1000s --tags=${TEST_FILE_NAME} 2>&1 | go-junit-report >"${TEST_FILE_NAME}.xml" 71 | 72 | fi 73 | 74 | popd 75 | } 76 | 77 | # @description: run tests for bicep 78 | # @param ${1}: test type, options; arm-ttk, pester 79 | # @param ${2}: test file name 80 | # @usage : source ${0} && bicep 81 | # @usage : source ${0} && bicep arm-ttk 82 | # @usage : source ${0} && bicep pester 83 | # @usage ; source ${0} && bicep pester End_To_End.Tests.ps1 84 | bicep() { 85 | source ./iac.bicep.sh 86 | 87 | pester() { 88 | _information "run end to end tests" 89 | 90 | pushd ./end_to_end 91 | # if the test file is not specified, run for all files 92 | if [ -z "${1}" ]; then 93 | pwsh -Command "Invoke-Pester -OutputFile test.xml -OutputFormat NUnitXML -EnableExit" 94 | else 95 | TEST_FILE=$(find ${1}) 96 | 97 | if [ ! -z "${TEST_FILE}" ]; then 98 | pwsh -Command "Invoke-Pester -OutputFile test.xml -OutputFormat NUnitXML ${TEST_FILE} -EnableExit" 99 | fi 100 | fi 101 | 102 | # return to the previous directory 103 | popd 104 | } 105 | 106 | # cd to the tests directory 107 | pushd ../../IAC/Bicep/test 108 | 109 | if [ -z "${1}" ]; then 110 | pester "$@" 111 | elif [ "${1}" == "pester" ]; then 112 | pester ${2} 113 | fi 114 | 115 | popd 116 | } 117 | -------------------------------------------------------------------------------- /scripts/utilities/http.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | _set_api_version() { 4 | uri=$1 5 | paas_version=$2 6 | 7 | echo "$1$2" 8 | } 9 | 10 | check_error_log() { 11 | if [[ -f "$AZDO_TEMP_LOG_PATH/http.error.log" ]]; then 12 | echo "" 13 | _error "Please check $AZDO_TEMP_LOG_PATH/http.error.log for install errors" 14 | fi 15 | } 16 | 17 | verify_response() { 18 | local _request_uri=$1 19 | local _response=$2 20 | 21 | if [[ "$_response" == *"innerException"* ]]; then 22 | { 23 | echo "----------------------------------------------" 24 | echo "Inner Exception in Http Response " 25 | echo "_request_uri: $_request_uri" 26 | echo "_response: " 27 | echo "$_response" 28 | } >>"$AZDO_TEMP_LOG_PATH/http.error.log" 29 | 30 | fi 31 | if [[ "$_response" == *"Azure DevOps Services | Sign In"* ]]; then 32 | { 33 | echo "----------------------------------------------" 34 | echo "Azure DevOps Services | Sign In Http Response (html login screen)" 35 | echo "_request_uri: $_request_uri" 36 | echo "_response: " 37 | echo "$_response" 38 | } >>"$AZDO_TEMP_LOG_PATH/http.error.log" 39 | fi 40 | if [[ "$_response" == *"Access Denied: The Personal Access Token used has expired"* ]]; then 41 | { 42 | echo "----------------------------------------------" 43 | echo "Access Denied: The Personal Access Token used has expired (html screen)" 44 | echo "_request_uri: $_request_uri" 45 | echo "_response: " 46 | echo "$_response" 47 | } >>"$AZDO_TEMP_LOG_PATH/http.error.log" 48 | fi 49 | } 50 | _debug_log_patch() { 51 | _debug "REQ - $1" 52 | _debug "RES - " 53 | _debug_json "$2" 54 | _debug "PAYLOAD -" 55 | _debug_json "$3" 56 | } 57 | 58 | request_patch() { 59 | request_uri=${1} 60 | payload=${2} 61 | content_type=${3} 62 | authorization=${4} 63 | 64 | _response=$(curl \ 65 | --silent \ 66 | --location \ 67 | --header "Content-Type: ${content_type}" \ 68 | --header "Authorization: ${authorization}" \ 69 | --request PATCH "${request_uri}" \ 70 | --data-raw "${payload}" \ 71 | --compressed) 72 | 73 | verify_response "$request_uri" "$_response" 74 | echo "$_response" 75 | } 76 | 77 | _debug_log_post() { 78 | _debug "REQ - $1" 79 | _debug "RES - " 80 | _debug_json "$2" 81 | _debug "PAYLOAD -" 82 | _debug_json "$3" 83 | } 84 | 85 | request_post() { 86 | request_uri=${1} 87 | payload=${2} 88 | content_type=${3} 89 | authorization=${4} 90 | 91 | _response=$(curl \ 92 | --silent \ 93 | --location \ 94 | --header "Content-Type: ${content_type}" \ 95 | --header "Authorization: ${authorization}" \ 96 | --request POST "${request_uri}" \ 97 | --data-raw "${payload}") 98 | 99 | verify_response "$request_uri" "$_response" 100 | echo "$_response" 101 | } 102 | 103 | request_put() { 104 | request_uri=${1} 105 | payload=${2} 106 | content_type=${3} 107 | authorization=${4} 108 | 109 | _response=$(curl \ 110 | --silent \ 111 | --location \ 112 | --header "Content-Type: ${content_type}" \ 113 | --header "Authorization: ${authorization}" \ 114 | --request PUT "${request_uri}" \ 115 | --data-raw "${payload}") 116 | 117 | verify_response "$request_uri" "$_response" 118 | echo "$_response" 119 | } 120 | 121 | _debug_log_get() { 122 | _debug "REQ - $1" 123 | _debug "RES - " 124 | _debug_json "$2" 125 | } 126 | 127 | request_get() { 128 | request_uri=${1} 129 | content_type=${2} 130 | authorization=${3} 131 | 132 | local _response 133 | 134 | _response=$(curl \ 135 | --silent \ 136 | --location \ 137 | --header "Content-Type: ${content_type}" \ 138 | --header "Authorization: ${authorization}" \ 139 | --request GET "${request_uri}") 140 | 141 | verify_response "$request_uri" "$_response" 142 | echo "$_response" 143 | } 144 | 145 | _debug_log_post_binary() { 146 | _debug "REQ - $1" 147 | _debug "RES - " 148 | _debug_json "$2" 149 | _debug "FILE_NAME - $3" 150 | } 151 | -------------------------------------------------------------------------------- /scripts/utilities/json.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ensure_json_file_exists() { 4 | local file=$1 5 | if [ ! -f "$file" ]; then 6 | echo "{}" >"$file" 7 | fi 8 | } 9 | 10 | get_json_value() { 11 | local file=$1 12 | local key=$2 13 | ensure_json_file_exists "$file" 14 | jq <"$file" -r ".$key" 15 | } 16 | 17 | set_json_value() { 18 | local file=$1 19 | local key=$2 20 | local value=$3 21 | ensure_json_file_exists "$file" 22 | 23 | updated_json=$(jq <$file --arg value "$value" --arg key "$key" '.[$key] = $value') 24 | echo -n "$updated_json" >"$file" 25 | } 26 | -------------------------------------------------------------------------------- /scripts/utilities/os.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | get_current_os() { 4 | echo "$OSTYPE" 5 | } 6 | 7 | check_linux_os() { 8 | if [[ $OSTYPE == *"linux"* ]]; then 9 | return 0 10 | fi 11 | return 1 12 | } 13 | 14 | check_mac_os() { 15 | if [[ $OSTYPE == *"darwin"* ]]; then 16 | return 0 17 | fi 18 | return 1 19 | } 20 | -------------------------------------------------------------------------------- /scripts/utilities/pipeline_logger.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Logger Functions with colors for Bash Shell 4 | 5 | _debug() { 6 | # Only print debug lines if debugging is turned on. 7 | if [ -n "${DEBUG_FLAG}" ]; then 8 | if [ -n "${GITHUB_ACTION}" ]; then 9 | echo "::debug::" "$@" 10 | elif [ -n "${SYSTEM_TEAMFOUNDATIONCOLLECTIONURI}" ]; then 11 | echo -e "##[debug]" "$@" 12 | else 13 | echo "DEBUG: " "$@" 14 | fi 15 | fi 16 | } 17 | 18 | _error() { 19 | if [ -n "${GITHUB_ACTION}" ]; then 20 | echo "::error::" "$@" 21 | elif [ -n "${SYSTEM_TEAMFOUNDATIONCOLLECTIONURI}" ]; then 22 | echo -e "##[error]" "$@" 23 | else 24 | echo "ERROR: " "$@" 25 | fi 26 | } 27 | 28 | _warning() { 29 | if [ -n "${GITHUB_ACTION}" ]; then 30 | echo "::warning::" "$@" 31 | elif [ -n "${SYSTEM_TEAMFOUNDATIONCOLLECTIONURI}" ]; then 32 | echo -e "##[warning]" "$@" 33 | else 34 | echo "WARNING: " "$@" 35 | fi 36 | } 37 | 38 | _information() { 39 | if [ -n "${GITHUB_ACTION}" ]; then 40 | echo "::notice::" "$@" 41 | elif [ -n "${SYSTEM_TEAMFOUNDATIONCOLLECTIONURI}" ]; then 42 | echo -e "##[command]" "$@" 43 | else 44 | echo "NOTICE: " "$@" 45 | fi 46 | } 47 | 48 | _success() { 49 | if [ -n "${GITHUB_ACTION}" ]; then 50 | echo "::notice::" "$@" 51 | elif [ -n "${SYSTEM_TEAMFOUNDATIONCOLLECTIONURI}" ]; then 52 | echo -e "##[section]" "$@" 53 | else 54 | echo "NOTICE: " "$@" 55 | fi 56 | } 57 | -------------------------------------------------------------------------------- /scripts/utilities/service_principal.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function loadServicePrincipalCredentials() { 4 | if [[ -f "$SYMPHONY_ENV_FILE_PATH" ]]; then 5 | SP_SUBSCRIPTION_ID=$(get_json_value "$SYMPHONY_ENV_FILE_PATH" "sp_sub_id") 6 | SP_TENANT_ID=$(get_json_value "$SYMPHONY_ENV_FILE_PATH" "sp_tenant_id") 7 | SP_ID=$(get_json_value "$SYMPHONY_ENV_FILE_PATH" "sp_client_id") 8 | fi 9 | 10 | if [ -z "$SP_SUBSCRIPTION_ID" ]; then 11 | _prompt_input "Enter Azure Service Principal Subscription Id" SP_SUBSCRIPTION_ID 12 | fi 13 | 14 | if [ -z "$SP_CLOUD_ENVIRONMENT" ]; then 15 | _prompt_input "Enter Azure Cloud Name" SP_CLOUD_ENVIRONMENT 16 | fi 17 | 18 | if [ -z "$SP_TENANT_ID" ]; then 19 | _prompt_input "Enter Azure Service Principal Tenant Id" SP_TENANT_ID 20 | fi 21 | 22 | if [ -z "$SP_ID" ]; then 23 | _prompt_input "Enter Azure Service Principal Client Id" SP_ID 24 | fi 25 | } 26 | 27 | # command is a global variable declared in the entrypoint script. 28 | function printEnvironment() { 29 | echo "" 30 | _information "********************************************************************" 31 | _information " Command: $command" 32 | if [[ "$command" == "pipeline" ]]; then 33 | _information " Orchestrator: $ORCHESTRATOR" 34 | _information " IAC Tool: $IACTOOL" 35 | fi 36 | _information " Subscription Id: $SP_SUBSCRIPTION_ID" 37 | _information " Tenant: $SP_TENANT_ID" 38 | _information " Client Id: $SP_ID" 39 | _information "Client Environment: $SP_CLOUD_ENVIRONMENT" 40 | _information "********************************************************************" 41 | echo "" 42 | } 43 | 44 | function load_symphony_env() { 45 | if [[ -f "$SYMPHONY_ENV_FILE_PATH" ]]; then 46 | SYMPHONY_RG_NAME=$(get_json_value "$SYMPHONY_ENV_FILE_PATH" "resource_group") 47 | SYMPHONY_SA_STATE_NAME=$(get_json_value "$SYMPHONY_ENV_FILE_PATH" "state_storage_account") 48 | SP_SUBSCRIPTION_ID=$(get_json_value "$SYMPHONY_ENV_FILE_PATH" "sp_sub_id") 49 | SP_TENANT_ID=$(get_json_value "$SYMPHONY_ENV_FILE_PATH" "sp_tenant_id") 50 | SP_ID=$(get_json_value "$SYMPHONY_ENV_FILE_PATH" "sp_client_id") 51 | SYMPHONY_EVENTS_TABLE_NAME=$(get_json_value "$SYMPHONY_ENV_FILE_PATH" "events_table_name") 52 | SYMPHONY_EVENTS_STORAGE_ACCOUNT=$(get_json_value "$SYMPHONY_ENV_FILE_PATH" "events_storage_account") 53 | SYMPHONY_SA_STATE_NAME_BACKUP=$(get_json_value "$SYMPHONY_ENV_FILE_PATH" "backupstate_storage_account") 54 | SYMPHONY_STATE_CONTAINER=$(get_json_value "$SYMPHONY_ENV_FILE_PATH" "state_container") 55 | 56 | fi 57 | 58 | if [ -z "$SYMPHONY_RG_NAME" ]; then 59 | _prompt_input "Enter Symphony Resource Group Name" SYMPHONY_RG_NAME 60 | fi 61 | 62 | if [ "$IACTOOL" != "bicep" ]; then 63 | if [ -z "$SYMPHONY_SA_STATE_NAME" ]; then 64 | _prompt_input "Enter Symphony State Storage Account Name" SYMPHONY_SA_STATE_NAME 65 | fi 66 | fi 67 | } 68 | -------------------------------------------------------------------------------- /scripts/utilities/shell_logger.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | _error() { 4 | printf " \e[31mError: %s\n\e[0m" "$@" 5 | } 6 | 7 | _fail() { 8 | step=$1 9 | printf "\e[31mFailed $step\n\e[0m" 10 | printf "\e[31mError: $2\n\e[0m" 11 | printf "\e[31mResources may have been deployed. Please run symphony destroy to clean up orphaned resources.\n\e[0m" 12 | exit 1 13 | } 14 | 15 | _danger() { 16 | printf " \e[31m%s\n\e[0m" "$@" 17 | } 18 | 19 | _prompt() { 20 | printf "\n\e[35m>%s\n\e[0m" "$@" 21 | } 22 | 23 | _debug() { 24 | #Only print debug lines if debugging is turned on. 25 | if [ "$DEBUG_FLAG" == true ]; then 26 | msg="$@" 27 | LIGHT_CYAN='\033[0;35m' 28 | NC='\033[0m' 29 | printf "DEBUG: ${NC} %s ${NC}\n" "${msg}" 30 | fi 31 | } 32 | 33 | _information() { 34 | printf "\e[36m%s\n\e[0m" "$@" 35 | } 36 | 37 | _success() { 38 | printf "\e[32m%s\n\e[0m" "$@" 39 | } 40 | 41 | _debug_json() { 42 | if [ -n "$DEBUG_FLAG" ]; then 43 | echo "$@" | jq 44 | fi 45 | } 46 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | declare INSTALL_PATH=$(pwd)/scripts/install/cli 3 | declare SCRIPTS_PATH=$(pwd)/scripts 4 | source "$SCRIPTS_PATH/utilities/shell_logger.sh" 5 | 6 | if [[ "$0" = "$BASH_SOURCE" ]]; then 7 | _error "WARNING: setup.sh should not executed directly. Please source this script." 8 | echo "" 9 | _information "source setup.sh" 10 | exit 1 11 | fi 12 | 13 | export PATH=$PATH:$INSTALL_PATH 14 | 15 | echo "" 16 | _success "Added Symphony temporarily ($INSTALL_PATH) to PATH" 17 | echo "" 18 | --------------------------------------------------------------------------------