diff --git a/.github/workflows/get-versions.ps1 b/.github/workflows/get-versions.ps1 new file mode 100644 index 00000000..f278cfdb --- /dev/null +++ b/.github/workflows/get-versions.ps1 @@ -0,0 +1,99 @@ +#Requires -Version 5 +param( + [Parameter(mandatory=$true)] + [string] + $PackageVersion +) + +function Invoke-SemanticSort { + param( + [Parameter(Mandatory=$true)] + [string[]] + $Versions + ) + + $major = @{label="major";expression={[int]$_.Split(".")[0]}} + $minor = @{label="minor";expression={[int]$_.Split(".")[1]}} + $patch = @{label="patch";expression={[int]$_.Split(".")[2]}} + + $Versions ` + | Select-Object $major, $minor, $patch ` + | Sort-Object major, minor, patch -Descending ` + | ForEach-Object { "$($_.major).$($_.minor).$($_.patch)" } +} + +function Get-NugetPackageVersion { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)] + [string] + $PackageName, + + [Parameter(Mandatory=$true)] + [string] + $PackageVersion, + + # URL for the release package feed + [string] + $ReleaseServiceIndex = "https://pkgs.dev.azure.com/ed-fi-alliance/Ed-Fi-Alliance-OSS/_packaging/EdFi%40Release/nuget/v3/index.json" + ) + + $nugetServicesURL = $ReleaseServiceIndex + + # The first URL just contains metadata for looking up more useful services + $nugetServices = Invoke-RestMethod $nugetServicesURL + + $packageService = $nugetServices.resources ` + | Where-Object { $_."@type" -like "PackageBaseAddress*" } ` + | Select-Object -Property "@id" -ExpandProperty "@id" + + # Pad the package version out to three part semver + switch ($PackageVersion.split(".").length) { + 1 { $versionSearch = "$PackageVersion.*.*"} + 2 { $versionSearch = "$PackageVersion.*" } + 3 { $versionSearch = $PackageVersion } + default: { throw "Invalid version string ``$($PackageVersion)``. Should be one, two, or three components from a Semantic Version.".Trim() } + } + $lowerId = $PackageName.ToLower() + + # Lookup available packages + $package = Invoke-RestMethod "$($packageService)$($lowerId)/index.json" + + # Sort by SemVer + $versions = Invoke-SemanticSort $package.versions + + # Find the first available version that matches the requested version + $version = $versions | Where-Object { $_ -like $versionSearch } | Select-Object -First 1 + + if ($null -eq $version) { + throw "Version ``$($PackageVersion)`` does not exist yet for ``$($PackageName)``." + } + + $version +} +Write-Host $PackageVersion +switch -Exact ($PackageVersion) +{ + '7.3' { $StandardVersion = "5.2.0" } + '7.2' { $StandardVersion = "5.1.0" } + '7.1' { $StandardVersion = "5.0.0" } +} + +$env:WEBAPI_INSTALLLER_VERSION = "$(Get-NugetPackageVersion -PackageName EdFi.Suite3.Installer.WebApi -PackageVersion $PackageVersion)".Trim() +$env:SWAGGER_INSTALLER_VERSION = "$(Get-NugetPackageVersion -PackageName EdFi.Suite3.Installer.SwaggerUI -PackageVersion $PackageVersion)".Trim() +$env:SANDBOXADMIN_INSTALLER_VERSION = "$(Get-NugetPackageVersion -PackageName EdFi.Suite3.Installer.SandboxAdmin -PackageVersion $PackageVersion)".Trim() + +$env:SWAGGER_VERSION = "$(Get-NugetPackageVersion -PackageName EdFi.Suite3.Ods.SwaggerUI -PackageVersion $PackageVersion)".Trim() +$env:SANDBOXADMIN_VERSION = "$(Get-NugetPackageVersion -PackageName EdFi.Suite3.Ods.SandboxAdmin -PackageVersion $PackageVersion)".Trim() + +if ($null -eq $StandardVersion) { + $env:DATABASE_INSTALLER_VERSION = "$(Get-NugetPackageVersion -PackageName EdFi.Suite3.RestApi.Databases -PackageVersion $PackageVersion)".Trim() + $env:DATABASE_INSTALLER = "EdFi.Suite3.RestApi.Databases" + $env:WEBAPI_VERSION = "$(Get-NugetPackageVersion -PackageName EdFi.Suite3.Ods.WebApi -PackageVersion $PackageVersion)".Trim() + $env:WEBAPI = "EdFi.Suite3.Ods.WebApi" +}else { + $env:DATABASE_INSTALLER_VERSION = "$(Get-NugetPackageVersion -PackageName EdFi.Suite3.RestApi.Databases.Standard.$StandardVersion -PackageVersion $PackageVersion)".Trim() + $env:DATABASE_INSTALLER = "EdFi.Suite3.RestApi.Databases.Standard.$StandardVersion" + $env:WEBAPI_VERSION = "$(Get-NugetPackageVersion -PackageName EdFi.Suite3.Ods.WebApi.Standard.$StandardVersion -PackageVersion $PackageVersion)".Trim() + $env:WEBAPI = "EdFi.Suite3.Ods.WebApi.Standard.$StandardVersion" +} diff --git a/.github/workflows/installation_scripts.yml b/.github/workflows/installation_scripts.yml new file mode 100644 index 00000000..77b87360 --- /dev/null +++ b/.github/workflows/installation_scripts.yml @@ -0,0 +1,193 @@ +name: Download packages and create installation scripts + +on: + workflow_dispatch: + inputs: + resource_group: + description: "The resource group of the VM" + required: true + web_api_vm_name: + description: "The name of the WEB API VM" + required: true + sql_vm_name: + description: "The name of the SQL VM" + required: true + ods_api_version: + description: "The version of EdFi ODS / API to install" + required: true + default: "7.3" + type: choice + options: + - 7.3 + - 7.2 + - 7.1 + - 6.2 + - 5.4 + db_server: + description: "Database server IP address" + required: true + +env: + ARM_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }} + ARM_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + ARM_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }} + ARM_TENANT_ID: ${{ vars.AZURE_TENANT_ID }} + +jobs: + run-command: + runs-on: ubuntu-latest + steps: + + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Get Package Versions + working-directory: ./.github/workflows + run: | + .\get-versions.ps1 -PackageVersion ${{ github.event.inputs.ods_api_version }} + shell: pwsh + + - name: Azure Login + uses: azure/login@v1 + with: + creds: '{"clientId":"${{ env.ARM_CLIENT_ID }}","clientSecret":"${{ env.ARM_CLIENT_SECRET }}","subscriptionId":"${{ env.ARM_SUBSCRIPTION_ID }}","tenantId":"${{ env.ARM_TENANT_ID }}"}' + + - name: Download Components on Web Server VM + run: | + az vm run-command invoke \ + --command-id RunPowerShellScript \ + --name "${{ github.event.inputs.web_api_vm_name }}" \ + --resource-group "${{ github.event.inputs.resource_group }}" \ + --scripts ' + # Download and extract WebApi Installer + Invoke-WebRequest -Uri "https://pkgs.dev.azure.com/ed-fi-alliance/Ed-Fi-Alliance-OSS/_apis/packaging/feeds/EdFi@Release/nuget/packages/EdFi.Suite3.Installer.WebApi/versions/${{ $env:WEBAPI_INSTALLLER_VERSION }}/content" -OutFile "C:/ODS/${{ github.event.inputs.ods_api_version }}/EdFi.Suite3.Installer.WebApi.${{ $env:WEBAPI_INSTALLLER_VERSION }}.nupkg" + Rename-Item -Path "C:/ODS/${{ github.event.inputs.ods_api_version }}/EdFi.Suite3.Installer.WebApi.${{ $env:WEBAPI_INSTALLLER_VERSION }}.nupkg" -NewName "EdFi.Suite3.Installer.WebApi.zip" + Expand-Archive -Path "C:/ODS/${{ github.event.inputs.ods_api_version }}/EdFi.Suite3.Installer.WebApi.zip" -DestinationPath "C:/ODS/${{ github.event.inputs.ods_api_version }}/EdFi.Suite3.Installer.WebApi" -Force + + # Download and extract SwaggerUI + Invoke-WebRequest -Uri "https://pkgs.dev.azure.com/ed-fi-alliance/Ed-Fi-Alliance-OSS/_apis/packaging/feeds/EdFi@Release/nuget/packages/EdFi.Suite3.Installer.SwaggerUI/versions/${{ $env:SWAGGER_INSTALLER_VERSION }}/content" -OutFile "C:/ODS/${{ github.event.inputs.ods_api_version }}/EdFi.Suite3.Installer.SwaggerUI.${{ $env:SWAGGER_INSTALLER_VERSION }}.nupkg" + Rename-Item -Path "C:/ODS/${{ github.event.inputs.ods_api_version }}/EdFi.Suite3.Installer.SwaggerUI.${{ $env:SWAGGER_INSTALLER_VERSION }}.nupkg" -NewName "EdFi.Suite3.Installer.SwaggerUI.zip" + Expand-Archive -Path "C:/ODS/${{ github.event.inputs.ods_api_version }}/EdFi.Suite3.Installer.SwaggerUI.zip" -DestinationPath "C:/ODS/${{ github.event.inputs.ods_api_version }}/EdFi.Suite3.Installer.SwaggerUI" -Force + + # Download and extract SandboxAdmin + Invoke-WebRequest -Uri "https://pkgs.dev.azure.com/ed-fi-alliance/Ed-Fi-Alliance-OSS/_apis/packaging/feeds/EdFi@Release/nuget/packages/EdFi.Suite3.Installer.SandboxAdmin/versions/${{ $env:SANDBOXADMIN_INSTALLER_VERSION }}/content" -OutFile "C:/ODS/${{ github.event.inputs.ods_api_version }}/EdFi.Suite3.Installer.SandboxAdmin.${{ $env:SANDBOXADMIN_INSTALLER_VERSION }}.nupkg" + Rename-Item -Path "C:/ODS/${{ github.event.inputs.ods_api_version }}/EdFi.Suite3.Installer.SandboxAdmin.${{ $env:SANDBOXADMIN_INSTALLER_VERSION }}.nupkg" -NewName "EdFi.Suite3.Installer.SandboxAdmin.zip" + Expand-Archive -Path "C:/ODS/${{ github.event.inputs.ods_api_version }}/EdFi.Suite3.Installer.SandboxAdmin.zip" -DestinationPath "C:/ODS/${{ github.event.inputs.ods_api_version }}/EdFi.Suite3.Installer.SandboxAdmin" -Force' + + - name: Download RestApi Databases on SQL Server VM + run: | + az vm run-command invoke \ + --command-id RunPowerShellScript \ + --name "${{ github.event.inputs.sql_vm_name }}" \ + --resource-group "${{ github.event.inputs.resource_group }}" \ + --scripts ' + # Download and extract RestApi Databases + Invoke-WebRequest -Uri "https://pkgs.dev.azure.com/ed-fi-alliance/Ed-Fi-Alliance-OSS/_apis/packaging/feeds/EdFi@Release/nuget/packages/${{ $env:DATABASE_INSTALLER }}/versions/${{ $env:DATABASE_INSTALLER_VERSION }}/content" -OutFile "C:/ODS/${{ github.event.inputs.ods_api_version }}/${{ $env:DATABASE_INSTALLER }}.nupkg" + Rename-Item -Path "C:/ODS/${{ github.event.inputs.ods_api_version }}/${{ $env:DATABASE_INSTALLER }}.nupkg" -NewName "EdFi.Suite3.RestApi.Databases.Standard.zip" + Expand-Archive -Path "C:/ODS/${{ github.event.inputs.ods_api_version }}/EdFi.Suite3.RestApi.Databases.Standard.zip" -DestinationPath "C:/ODS/${{ github.event.inputs.ods_api_version }}/${{ $env:DATABASE_INSTALLER }}" -Force + Remove-Item -Path "C:/ODS/${{ github.event.inputs.ods_api_version }}/${{ $env:DATABASE_INSTALLER }}/configuration.postgreSQL.json" -Force' + + - name: Create Deployment Script + run: | + SCRIPT_CONTENT=' + Set-Location "C:/ODS/${{ github.event.inputs.ods_api_version }}/${{ $env:DATABASE_INSTALLER }}" + + Write-Host "Creating ps1 file..." + "Import-Module .\Deployment.psm1" | Out-File -FilePath "dbs_installation.ps1" + "Initialize-DeploymentEnvironment" | Out-File -FilePath "dbs_installation.ps1" -Append + + Write-Host "File created successfully" + Get-Content -Path "dbs_installation.ps1"' + + az vm run-command invoke \ + --command-id RunPowerShellScript \ + --name "${{ github.event.inputs.sql_vm_name }}" \ + --resource-group "${{ github.event.inputs.resource_group }}" \ + --scripts "$SCRIPT_CONTENT" + + - name: Create WebApi Installation Script + run: | + SCRIPT_CONTENT=' + Set-Location "C:/ODS/${{ github.event.inputs.ods_api_version }}/EdFi.Suite3.Installer.WebApi" + + Write-Host "Creating WebApi installation script..." + @" + Import-Module .\Install-EdFiOdsWebApi.psm1 + `$parameters = @{ + PackageVersion = "${{ $env:WEBAPI_VERSION }}" + PackageName = "${{ $env:WEBAPI }}" + DbConnectionInfo = @{ + Engine = "SqlServer" + Server = "${{ github.event.inputs.db_server }}" + Username = "${{ secrets.SQL_ADMIN_USERNAME }}" + Password = "${{ secrets.SQL_ADMIN_PASSWORD }}" + UseIntegratedSecurity = `$false + } + IsSandbox = `$true + UnEncryptedConnection = `$true + } + Install-EdFiOdsWebApi @parameters + "@ | Out-File -FilePath "webapi_installation.ps1" + + Write-Host "File created successfully" + Get-Content -Path "webapi_installation.ps1"' + + az vm run-command invoke \ + --command-id RunPowerShellScript \ + --name "${{ github.event.inputs.web_api_vm_name }}" \ + --resource-group "${{ github.event.inputs.resource_group }}" \ + --scripts "$SCRIPT_CONTENT" + + - name: Create SwaggerUI Installation Script + run: | + SCRIPT_CONTENT=' + Set-Location "C:/ODS/${{ github.event.inputs.ods_api_version }}/EdFi.Suite3.Installer.SwaggerUI" + + Write-Host "Creating ps1 file..." + @" + Import-Module .\Install-EdFiOdsSwaggerUI.psm1 + `$parameters = @{ + PackageVersion = "${{ $env:SWAGGER_VERSION }}" + WebApiVersionUrl = "https://edfi-ods/WebApi" + PrePopulatedKey = "${{ secrets.POPULATED_KEY }}" + PrePopulatedSecret = "${{ secrets.POPULATED_SECRET }}" + } + Install-EdFiOdsSwaggerUI @parameters + "@ | Out-File -FilePath "swaggerui_installation.ps1" + + Write-Host "File created successfully" + Get-Content -Path "swaggerui_installation.ps1" + ' + + az vm run-command invoke \ + --command-id RunPowerShellScript \ + --name "${{ github.event.inputs.web_api_vm_name }}" \ + --resource-group "${{ github.event.inputs.resource_group }}" \ + --scripts "$SCRIPT_CONTENT" + + - name: Create SandboxAdmin Installation Script + run: | + SCRIPT_CONTENT=' + Set-Location "C:/ODS/${{ github.event.inputs.ods_api_version }}/EdFi.Suite3.Installer.SandboxAdmin" + + Write-Host "Creating ps1 file..." + @" + Import-Module .\Install-EdFiOdsSandboxAdmin.psm1 + `$parameters = @{ + PackageVersion = "${{ $env:SANDBOXADMIN_VERSION }}" + WebApiVersionUrl = "https://edfi-ods/WebApi" + PrePopulatedKey = "${{ secrets.POPULATED_KEY }}" + PrePopulatedSecret = "${{ secrets.POPULATED_SECRET }}" + } + Install-EdFiOdsSandboxAdmin @parameters + "@ | Out-File -FilePath "sandboxadmin_installation.ps1" + + Write-Host "File created successfully" + Get-Content -Path "sandboxadmin_installation.ps1" + ' + + az vm run-command invoke \ + --command-id RunPowerShellScript \ + --name "${{ github.event.inputs.web_api_vm_name }}" \ + --resource-group "${{ github.event.inputs.resource_group }}" \ + --scripts "$SCRIPT_CONTENT" diff --git a/.github/workflows/terraform-deploy.yml b/.github/workflows/terraform-deploy.yml index 4b8741ae..948da651 100644 --- a/.github/workflows/terraform-deploy.yml +++ b/.github/workflows/terraform-deploy.yml @@ -15,11 +15,6 @@ on: description: 'Environment Label' required: true default: 'ods-perf-test' - environment: - description: 'Environment to deploy to' - required: true - type: environment - default: 'development' permissions: contents: read @@ -34,7 +29,6 @@ jobs: terraform: name: 'Terraform' runs-on: ubuntu-latest - environment: ${{ github.event.inputs.environment }} defaults: run: @@ -75,7 +69,7 @@ jobs: - name: Terraform Plan run: terraform plan -out=tfplan - + - name: Terraform Apply run: terraform apply -auto-approve tfplan @@ -85,31 +79,31 @@ jobs: # Debug: Show terraform state content echo "Terraform state content:" cat terraform.tfstate - + # Get storage account name using jq for reliable JSON parsing STORAGE_ACCOUNT=$(cat terraform.tfstate | jq -r '.outputs.azure_storage_tfstate.value') - + echo "Extracted storage account name: $STORAGE_ACCOUNT" - + # Validate the storage account name if [ -z "$STORAGE_ACCOUNT" ]; then echo "Error: Failed to extract storage account name" exit 1 fi - + if [ ${#STORAGE_ACCOUNT} -gt 24 ]; then echo "Error: Storage account name too long: ${#STORAGE_ACCOUNT} characters" exit 1 fi - + if [[ ! $STORAGE_ACCOUNT =~ ^[a-z0-9]*$ ]]; then echo "Error: Storage account name contains invalid characters" exit 1 fi - + # Set the output echo "storage_account=${STORAGE_ACCOUNT}" >> $GITHUB_OUTPUT - + # Debug: Show what we're setting echo "Setting storage account to: ${STORAGE_ACCOUNT}" shell: bash @@ -118,9 +112,9 @@ jobs: run: | # Create a more descriptive state file name STATE_FILE="${{ github.event.inputs.prefix }}-${{ github.event.inputs.label }}.tfstate" - + echo "Using state file name: ${STATE_FILE}" - + # Create the backend configuration cat > backend.tf << EOF terraform { @@ -132,7 +126,7 @@ jobs: } } EOF - + # Verify the content echo "Created backend.tf content:" cat backend.tf @@ -141,9 +135,9 @@ jobs: run: | echo "Backend configuration:" cat backend.tf - + terraform init -migrate-state -force-copy || { echo "Migration failed. Current directory contents:" ls -la exit 1 - } \ No newline at end of file + } diff --git a/eng/terraform/README.md b/eng/terraform/README.md index 1341cc2f..00c2dfbd 100644 --- a/eng/terraform/README.md +++ b/eng/terraform/README.md @@ -63,6 +63,8 @@ environment and register the next values - SQL_ADMIN_USERNAME - WEB_ADMIN_PASSWORD - WEB_ADMIN_USERNAME + - POPULATED_KEY + - POPULATED_SECRET ``` 3. Environment variables ``` diff --git a/eng/terraform/install-web-server-prerequisites.ps1 b/eng/terraform/install-web-server-prerequisites.ps1 index 5f528f08..4439cc83 100644 --- a/eng/terraform/install-web-server-prerequisites.ps1 +++ b/eng/terraform/install-web-server-prerequisites.ps1 @@ -86,19 +86,47 @@ function Install-DotNetHosting { Start-Transcript -Path $LogFile -Append - # Install IIS Web Server Role - Write-Host "Installing IIS Web Server Role..." - Enable-WindowsOptionalFeature -Online -FeatureName IIS-WebServerRole -NoRestart | Out-Null + # Install IIS Web Server Role withcommon features + Write-Host "Installing IIS Web Server Role and features..." + $features = @( + "IIS-WebServerRole", + "IIS-WebServer", + "IIS-CommonHttpFeatures", + "IIS-DefaultDocument", + "IIS-DirectoryBrowsing", + "IIS-HttpErrors", + "IIS-StaticContent", + "IIS-HttpRedirect", + "IIS-HealthAndDiagnostics", + "IIS-HttpLogging", + "IIS-LoggingLibraries", + "IIS-RequestMonitor", + "IIS-Security", + "IIS-RequestFiltering", + "IIS-HttpCompressionStatic", + "IIS-WebServerManagementTools", + "IIS-ManagementConsole", + "IIS-BasicAuthentication", + "IIS-WindowsAuthentication", + "IIS-ApplicationInit", + "IIS-NetFxExtensibility45", + "IIS-ASPNET45" + ) - # Check if IIS was installed successfully - if ($?) { - Write-Host "IIS installed successfully." - } else { - Write-Error "Failed to install IIS." - Stop-Transcript - exit 1 + foreach ($feature in $features) { + Write-Host "Installing feature: $feature" + Enable-WindowsOptionalFeature -Online -FeatureName $feature -All -NoRestart | Out-Null + if ($?) { + Write-Host "$feature installed successfully." + } else { + Write-Error "Failed to install $feature." + Stop-Transcript + exit 1 + } } + Write-Host "IIS installation completed successfully." + # Install .NET 8.0 Hosting Bundle via Chocolatey Write-Host "Installing .NET 8.0 Hosting Bundle..." $common_args = @('-y', '--no-progress') @@ -128,6 +156,12 @@ Invoke-RefreshPath Enable-LongFileNames Install-Choco Install-PowerShellTools + +# Remove all existing .NET Core and ASP.NET Core components before installing .NET 8.0 +Write-Host "Removing existing .NET components..." +Get-ChildItem -Path "C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\" -Directory | Remove-Item -Recurse -Force +Get-ChildItem -Path "C:\Program Files\dotnet\shared\Microsoft.NETCore.App\" -Directory | Remove-Item -Recurse -Force + $applicationSetupLog = "$PSScriptRoot/application-setup.log" Install-DotNetHosting -LogFile $applicationSetupLog &choco install vcredist140 @common_args