diff --git a/.devcontainer/createorupdate.ps1 b/.devcontainer/createorupdate.ps1 index 243948a..10a775b 100755 --- a/.devcontainer/createorupdate.ps1 +++ b/.devcontainer/createorupdate.ps1 @@ -1,21 +1,11 @@ #!/usr/bin/env pwsh # Runs post create commands to prep Codespace for project -# Update relevant packages +# Get latest package information sudo apt-get update -#sudo apt-get install --only-upgrade -y azure-cli powershell -if (!(Get-Command func -ErrorAction SilentlyContinue)) { - sudo apt-get install -y azure-functions-core-tools -} -if (!(Get-Content /etc/apt/sources.list | Select-String "^deb.*hashicorp" )) { - sudo apt-get install -y lsb-release - curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add - - sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main" - sudo apt-get install -y terraform -} -if (!(Get-Command tmux -ErrorAction SilentlyContinue)) { - sudo apt-get install -y tmux -} + +# Install tfenv dependencies +sudo apt-get install -y tmux # Determine directory locations (may vary based on what branch has been cloned initially) $repoDirectory = (Split-Path $PSScriptRoot -Parent) @@ -28,7 +18,8 @@ if (!(Get-Command tfenv -ErrorAction SilentlyContinue)) { Write-Host 'Installing tfenv...' git clone https://github.com/tfutils/tfenv.git ~/.tfenv sudo ln -s ~/.tfenv/bin/* /usr/local/bin -} else { +} +else { Write-Host 'Upgrading tfenv...' git -C ~/.tfenv pull } @@ -42,19 +33,6 @@ tfenv use latest terraform init -upgrade Pop-Location -# Use geekzter/bootstrap-os for PowerShell setup -if (Test-Path ~/bootstrap-os) { - # This has been run before, upgrade packages - sudo apt-get upgrade -y -} else { - git clone https://github.com/geekzter/bootstrap-os.git ~/bootstrap-os -} -Push-Location ~/bootstrap-os/linux -./bootstrap_linux.sh --skip-packages -Pop-Location -. ~/bootstrap-os/common/functions/functions.ps1 -AddorUpdateModule Posh-Git - # Link PowerShell Profile if (!(Test-Path $Profile)) { New-Item -ItemType symboliclink -Path $Profile -Target $profileTemplate -Force | Select-Object -ExpandProperty Name diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 926f5b6..a7973e5 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,20 +1,20 @@ { + "features": { + "ghcr.io/devcontainers/features/azure-cli:1": {}, + "ghcr.io/jlaundry/devcontainer-features/azure-functions-core-tools:1": {}, + "ghcr.io/devcontainers/features/dotnet:2": {}, + "ghcr.io/devcontainers/features/powershell:1": {}, + "ghcr.io/devcontainers/features/terraform:1": {} + }, // Install extensions - "extensions": [ - "4ops.terraform", - "ms-azuretools.vscode-azurefunctions", - "ms-azuretools.vscode-azureterraform", - "ms-dotnettools.csharp", - "ms-vscode.azurecli", - "ms-vscode.PowerShell" - ], - - // VSCode settings - "settings": { - // "terminal.integrated.cwd": "/home/codespace/workspace, - "terminal.integrated.shell.linux": "/usr/bin/pwsh" + "customizations": { + "vscode": { + "settings": { + // "terminal.integrated.cwd": "/home/codespace/workspace, + "terminal.integrated.shell.linux": "/usr/bin/pwsh" + } + } }, - // Run bash script in .devcontainer directory "postCreateCommand": "/usr/bin/pwsh -nop -f ./.devcontainer/createorupdate.ps1 > ~/post-create.log" -} \ No newline at end of file +} diff --git a/.devcontainer/profile.ps1 b/.devcontainer/profile.ps1 index 1bd5436..fc63048 100644 --- a/.devcontainer/profile.ps1 +++ b/.devcontainer/profile.ps1 @@ -8,7 +8,7 @@ $scriptDirectory = (Join-Path $repoDirectory "scripts") [System.Collections.ArrayList]$pathList = $env:PATH.Split(":") # Insert script path into PATH, so scripts can be called from anywhere if (!$pathList.Contains($scriptDirectory)) { - $pathList.Insert(1,$scriptDirectory) + $pathList.Insert(1, $scriptDirectory) } $env:PATH = $pathList -Join ":" diff --git a/.github/workflows/ci-dotnet.yml b/.github/workflows/ci-dotnet.yml index 9673c86..dc99b82 100644 --- a/.github/workflows/ci-dotnet.yml +++ b/.github/workflows/ci-dotnet.yml @@ -3,50 +3,46 @@ name: ci-dotnet on: push: branches: - - '**' + - "**" paths: - - '.github/workflows/ci-function.yml' - - 'functions/**' + - ".github/workflows/ci-function.yml" + - "functions/**" pull_request: - branches: [ main ] + branches: [main] paths-ignore: - - '**/README.md' - - '**/LICENSE' - - 'visuals/**' + - "**/README.md" + - "**/LICENSE" + - "visuals/**" schedule: - - cron: '0 2 * * *' + - cron: "0 2 * * *" # Allows you to run this workflow manually from the Actions tab workflow_dispatch: env: - AZURE_FUNCTIONAPP_PACKAGE_PATH: './functions' - DOTNET_VERSION: '6.0.x' + AZURE_FUNCTIONAPP_PACKAGE_PATH: "./functions" jobs: dotnet: - runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Setup .NET - uses: actions/setup-dotnet@v1 - with: - dotnet-version: ${{ env.DOTNET_VERSION }} - - name: Restore dependencies - run: | - pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' - dotnet restore - popd - - name: Build - run: | - pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' - dotnet build --no-restore --output ./bin/publish - popd - - name: Test - run: | - pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' - dotnet test --no-build --verbosity normal - popd \ No newline at end of file + - uses: actions/checkout@v4 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + - name: Restore dependencies + run: | + pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' + dotnet restore + popd + - name: Build + run: | + pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' + dotnet build --no-restore --output ./bin/publish + popd + - name: Test + run: | + pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' + dotnet test --no-build --verbosity normal + popd diff --git a/.github/workflows/ci-scripted-strategy.json b/.github/workflows/ci-scripted-strategy.json index 5a659fd..8903acf 100644 --- a/.github/workflows/ci-scripted-strategy.json +++ b/.github/workflows/ci-scripted-strategy.json @@ -1,11 +1,6 @@ { - "os": [ - "ubuntu-latest" - ], - "compatible": [ - "backward", - "forward" - ], + "os": ["ubuntu-latest"], + "compatible": ["backward", "forward"], "include": [ { "name": "a", @@ -30,4 +25,4 @@ "upgrade_azure_cli": true } ] -} \ No newline at end of file +} diff --git a/.github/workflows/ci-scripted.yml b/.github/workflows/ci-scripted.yml index 55f09aa..2e1d927 100644 --- a/.github/workflows/ci-scripted.yml +++ b/.github/workflows/ci-scripted.yml @@ -1,47 +1,47 @@ on: pull_request: - branches: [ main ] + branches: [main] paths-ignore: - - '**/README.md' - - '**/LICENSE' - - 'visuals/**' + - "**/README.md" + - "**/LICENSE" + - "visuals/**" schedule: - - cron: '0 2 * * Thu,Sat' + - cron: "0 2 * * Thu,Sat" # Allows you to run this workflow manually from the Actions tab workflow_dispatch: inputs: upgrade_provider_versions: - description: 'Upgrade Terraform provider versions' + description: "Upgrade Terraform provider versions" required: false type: boolean default: false upgrade_terraform_version: - description: 'Upgrade Terraform version' + description: "Upgrade Terraform version" required: false type: boolean default: false upgrade_azure_cli: - description: 'Upgrade Azure CLI to latest version' + description: "Upgrade Azure CLI to latest version" required: false type: boolean default: false destroy: - description: 'Destroy Infrastucture' + description: "Destroy Infrastucture" required: false type: boolean default: true workspace: type: choice required: true - description: 'Terraform Workspace' + description: "Terraform Workspace" default: ci - options: - - ci - - ci1 - - ci2 - - ci3 + options: + - ci + - ci1 + - ci2 + - ci3 env: ARM_SAS_TOKEN: ${{ secrets.ARM_SAS_TOKEN }} TF_IN_AUTOMATION: true @@ -63,38 +63,38 @@ jobs: working-directory: scripts runs-on: ubuntu-latest steps: - - name: Checkout source - uses: actions/checkout@v4 - - - name: Print environment variables - run: | - Get-ChildItem -Path Env: -Recurse -Include ARM_*,AZURE_*,GITHUB_*,INPUT_*,TF_* | Sort-Object -Property Name - shell: pwsh - - - name: Create matrix - id: create-matrix - run: | - ConvertTo-SecureString -AsPlainText -String '${{ secrets.GITHUB_TOKEN }}' | Set-Variable gitHubToken - ./create_workflow_strategy.ps1 -GitHubToken $gitHubToken ` - -DestroyInput "${{ github.event.inputs.destroy }}" ` - -UseLatestAzureCLIVersionInput "${{ github.event.inputs.upgrade_azure_cli }}" ` - -UseLatestTerraformProviderVersionsInput "${{ github.event.inputs.upgrade_provider_versions }}" ` - -UseLatestTerraformVersionInput "${{ github.event.inputs.upgrade_terraform_version }}" - - shell: pwsh - - - name: Install json2yaml - run: | - sudo npm install -g json2yaml - - - name: Display matrix - run: | - $matrixJSON = '${{ steps.create-matrix.outputs.matrix }}' - $matrixJSON - $matrixJSON | jq - $matrixJSON | ConvertFrom-Json - $matrixJSON | json2yaml - shell: pwsh + - name: Checkout source + uses: actions/checkout@v4 + + - name: Print environment variables + run: | + Get-ChildItem -Path Env: -Recurse -Include ARM_*,AZURE_*,GITHUB_*,INPUT_*,TF_* | Sort-Object -Property Name + shell: pwsh + + - name: Create matrix + id: create-matrix + run: | + ConvertTo-SecureString -AsPlainText -String '${{ secrets.GITHUB_TOKEN }}' | Set-Variable gitHubToken + ./create_workflow_strategy.ps1 -GitHubToken $gitHubToken ` + -DestroyInput "${{ github.event.inputs.destroy }}" ` + -UseLatestAzureCLIVersionInput "${{ github.event.inputs.upgrade_azure_cli }}" ` + -UseLatestTerraformProviderVersionsInput "${{ github.event.inputs.upgrade_provider_versions }}" ` + -UseLatestTerraformVersionInput "${{ github.event.inputs.upgrade_terraform_version }}" + + shell: pwsh + + - name: Install json2yaml + run: | + sudo npm install -g json2yaml + + - name: Display matrix + run: | + $matrixJSON = '${{ steps.create-matrix.outputs.matrix }}' + $matrixJSON + $matrixJSON | jq + $matrixJSON | ConvertFrom-Json + $matrixJSON | json2yaml + shell: pwsh outputs: matrix: ${{ steps.create-matrix.outputs.matrix }} @@ -115,188 +115,187 @@ jobs: runs-on: ${{ matrix.os }} steps: - - name: Checkout source - uses: actions/checkout@v4 - - - name: Dump strategy context - env: - MATRIX_CONTEXT: ${{ toJSON(matrix) }} - STRATEGY_CONTEXT: ${{ toJSON(strategy) }} - run: | - Write-Host "Strategy context:" - $env:STRATEGY_CONTEXT | ConvertFrom-Json | Format-List - Write-Host "Matrix context:" - $env:MATRIX_CONTEXT | ConvertFrom-Json | Format-List - shell: pwsh - - - name: Upgrade Azure CLI - id: az-upgrade - if: ${{ matrix.upgrade_azure_cli }} - run: | - # sudo az upgrade -y # not reliable - - curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null - echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/azure-cli.list - sudo apt-get update - - $packageName = ($(apt-cache show azure-cli | Select-String 'Version: ${{ matrix.azure_cli_version }}.*$') -split ' ' | Select-Object -Last 1) - Write-Host "`nAzure CLI package: $packageName" - if ($packageName) { - sudo apt-get install --allow-downgrades azure-cli=$packageName - } else { - Write-Host "Azure CLI version ${{ matrix.azure_cli_version }} not found" - } - - az -v - continue-on-error: true - shell: pwsh - - - name: Use Azure CLI - uses: azure/login@v2 - with: - creds: ${{ secrets.AZURE_CREDENTIALS }} - enable-AzPSSession: false - - - name: Show Azure CLI version - run: | - az -v - - - name: Use Terraform - uses: hashicorp/setup-terraform@v3 - with: - terraform_version: ${{matrix.terraform_version}} - terraform_wrapper: false - - - name: Show Terraform version - run: | - terraform -v - - - name: Unpin Terraform provider versions - id: terraform-version-check - if: ${{ !matrix.pin_provider_versions }} - run: | - Set-Location (Join-Path $env:GITHUB_WORKSPACE terraform) # Task does not support 'working-directory' property - (Get-Content ./provider.tf) -replace " = `" *= +",' = "~> ' | Out-File provider.tf - Get-Content ./provider.tf - if (Test-Path .terraform.lock.hcl) { - Remove-Item .terraform.lock.hcl -Force - } - shell: pwsh - - - name: Prepare environment variables - env: - AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} - ENVIRONMENT_VARIABLES: ${{ secrets.ENVIRONMENT_VARIABLES }} - run: | - # Parse Azure secret into Terraform variables - $servicePrincipal = ($env:AZURE_CREDENTIALS | ConvertFrom-Json) - $env:ARM_CLIENT_ID = $servicePrincipal.clientId - $env:ARM_CLIENT_SECRET = $servicePrincipal.clientSecret - $env:ARM_SUBSCRIPTION_ID = $servicePrincipal.subscriptionId - $env:ARM_TENANT_ID = $servicePrincipal.tenantId - - $env:TF_VAR_run_id=$env:GITHUB_RUN_ID - # We may not be able to create a Service Principal with a Service Principal, reuse Terraform SP for Logic App: - $env:TF_VAR_workflow_sp_application_id = $servicePrincipal.clientId - $env:TF_VAR_workflow_sp_application_secret = $servicePrincipal.clientSecret - $env:TF_VAR_workflow_sp_object_id = $servicePrincipal.objectId - - # Save environment variable setup for subsequent steps - Get-ChildItem -Path Env: -Recurse -Include ARM_*,TF_VAR_* | ForEach-Object {Write-Output "$($_.Name)=$($_.Value)"} >> $env:GITHUB_ENV - shell: pwsh - - - name: Show environment variables - run: | - Write-Host "Environment (sorted):" - Get-ChildItem -Path Env: -Recurse -Include ARM_*,AZURE_*,GITHUB_*,INPUT_*,TF_* | Sort-Object -Property Name - Write-Host "Environment (unsorted):" - Get-ChildItem -Path Env: -Recurse -Include ARM_*,AZURE_*,GITHUB_*,INPUT_*,TF_* - shell: pwsh - - - name: Terraform Init & Plan - run: ./deploy.ps1 -Init -Plan - shell: pwsh - - - name: Use Node.js - if: ${{ matrix.terraform_apply }} - uses: actions/setup-node@v4 - - name: Install Azure Functions Core Tools - if: ${{ matrix.terraform_apply }} - run: - npm i -g azure-functions-core-tools@3 --unsafe-perm true - - - name: Terraform Apply - id: terraform-apply - if: ${{ matrix.terraform_apply }} - run: ./deploy.ps1 -Apply -Force -NoCode - shell: pwsh - - - name: Deploy Azure Function - if: ${{ matrix.terraform_apply }} - run: ./deploy_functions.ps1 - shell: pwsh - - - name: Test connection to Minecraft Server - if: ${{ matrix.terraform_apply }} - run: | - Set-Location (Join-Path $env:GITHUB_WORKSPACE terraform) # Task does not support 'working-directory' property - $minecraftConfig = (terraform output -json minecraft_java | ConvertFrom-Json -AsHashtable) - foreach ($minecraftConfigName in $minecraftConfig.Keys) { - Write-Host "Processing configuration '{$minecraftConfigName}''..." - $containerGroupID = $minecraftConfig[$minecraftConfigName].container_group_id - $serverFQDN = $minecraftConfig[$minecraftConfigName].minecraft_server_fqdn - $serverPort = $minecraftConfig[$minecraftConfigName].minecraft_server_port - - # Wait for Minecraft socket to open - $connectionAttempts = 0 - do { - Start-Sleep -Seconds 10 - Write-Host "Pinging ${serverFQDN} on port ${serverPort}..." - try { - $connectionAttempts++ - $mineCraftConnection = New-Object System.Net.Sockets.TcpClient($serverFQDN, $serverPort) -ErrorAction SilentlyContinue - } catch [System.Management.Automation.MethodInvocationException] { - Write-Warning $_ - } - } while ((!$mineCraftConnection || !$mineCraftConnection.Connected) -and ($connectionAttempts -le 10)) - if ($mineCraftConnection.Connected) { - Write-Host "Connected to ${serverFQDN}:${serverPort}" - $mineCraftConnection.Close() + - name: Checkout source + uses: actions/checkout@v4 + + - name: Dump strategy context + env: + MATRIX_CONTEXT: ${{ toJSON(matrix) }} + STRATEGY_CONTEXT: ${{ toJSON(strategy) }} + run: | + Write-Host "Strategy context:" + $env:STRATEGY_CONTEXT | ConvertFrom-Json | Format-List + Write-Host "Matrix context:" + $env:MATRIX_CONTEXT | ConvertFrom-Json | Format-List + shell: pwsh + + - name: Upgrade Azure CLI + id: az-upgrade + if: ${{ matrix.upgrade_azure_cli }} + run: | + # sudo az upgrade -y # not reliable + + curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null + echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/azure-cli.list + sudo apt-get update + + $packageName = ($(apt-cache show azure-cli | Select-String 'Version: ${{ matrix.azure_cli_version }}.*$') -split ' ' | Select-Object -Last 1) + Write-Host "`nAzure CLI package: $packageName" + if ($packageName) { + sudo apt-get install --allow-downgrades azure-cli=$packageName } else { - Write-Warning "Could not connect to ${serverFQDN}:${serverPort}!" + Write-Host "Azure CLI version ${{ matrix.azure_cli_version }} not found" + } + + az -v + continue-on-error: true + shell: pwsh + + - name: Use Azure CLI + uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + enable-AzPSSession: false + + - name: Show Azure CLI version + run: | + az -v + + - name: Use Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: ${{matrix.terraform_version}} + terraform_wrapper: false + + - name: Show Terraform version + run: | + terraform -v + + - name: Unpin Terraform provider versions + id: terraform-version-check + if: ${{ !matrix.pin_provider_versions }} + run: | + Set-Location (Join-Path $env:GITHUB_WORKSPACE terraform) # Task does not support 'working-directory' property + (Get-Content ./provider.tf) -replace " = `" *= +",' = "~> ' | Out-File provider.tf + Get-Content ./provider.tf + if (Test-Path .terraform.lock.hcl) { + Remove-Item .terraform.lock.hcl -Force } - - # BUG: https://github.com/Azure/azure-cli/issues/13352 - # No tty / ioctl device - # az container exec --ids $containerGroupID --exec-command "/health.sh" --container-name minecraft - script --return --quiet -c "az container exec --ids $containerGroupID --exec-command '/health.sh' --container-name minecraft" /dev/null - - az container show --ids $containerGroupID --query instanceView - az container logs --ids $containerGroupID - } - shell: pwsh - - - name: Remove backup items & resource locks - if: ${{ matrix.destroy }} - run: | - . (Join-Path $env:GITHUB_WORKSPACE scripts functions.ps1) - TearDown-Resources -Backups -Locks - shell: pwsh - - - name: Terraform Destroy - if: ${{ matrix.destroy }} - run: ./deploy.ps1 -Destroy -Force - shell: pwsh - continue-on-error: ${{ matrix.ignore_destroy_failure }} - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - if: ${{ matrix.terraform_apply }} - with: - name: terraform${{ matrix.name }} - path: 'terraform' - - - name: Teardown - if: ${{ matrix.destroy || failure() }} - run: ./deploy.ps1 -Init -Teardown - shell: pwsh + shell: pwsh + + - name: Prepare environment variables + env: + AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} + ENVIRONMENT_VARIABLES: ${{ secrets.ENVIRONMENT_VARIABLES }} + run: | + # Parse Azure secret into Terraform variables + $servicePrincipal = ($env:AZURE_CREDENTIALS | ConvertFrom-Json) + $env:ARM_CLIENT_ID = $servicePrincipal.clientId + $env:ARM_CLIENT_SECRET = $servicePrincipal.clientSecret + $env:ARM_SUBSCRIPTION_ID = $servicePrincipal.subscriptionId + $env:ARM_TENANT_ID = $servicePrincipal.tenantId + + $env:TF_VAR_run_id=$env:GITHUB_RUN_ID + # We may not be able to create a Service Principal with a Service Principal, reuse Terraform SP for Logic App: + $env:TF_VAR_workflow_sp_application_id = $servicePrincipal.clientId + $env:TF_VAR_workflow_sp_application_secret = $servicePrincipal.clientSecret + $env:TF_VAR_workflow_sp_object_id = $servicePrincipal.objectId + + # Save environment variable setup for subsequent steps + Get-ChildItem -Path Env: -Recurse -Include ARM_*,TF_VAR_* | ForEach-Object {Write-Output "$($_.Name)=$($_.Value)"} >> $env:GITHUB_ENV + shell: pwsh + + - name: Show environment variables + run: | + Write-Host "Environment (sorted):" + Get-ChildItem -Path Env: -Recurse -Include ARM_*,AZURE_*,GITHUB_*,INPUT_*,TF_* | Sort-Object -Property Name + Write-Host "Environment (unsorted):" + Get-ChildItem -Path Env: -Recurse -Include ARM_*,AZURE_*,GITHUB_*,INPUT_*,TF_* + shell: pwsh + + - name: Terraform Init & Plan + run: ./deploy.ps1 -Init -Plan + shell: pwsh + + - name: Use Node.js + if: ${{ matrix.terraform_apply }} + uses: actions/setup-node@v4 + - name: Install Azure Functions Core Tools + if: ${{ matrix.terraform_apply }} + run: npm i -g azure-functions-core-tools@3 --unsafe-perm true + + - name: Terraform Apply + id: terraform-apply + if: ${{ matrix.terraform_apply }} + run: ./deploy.ps1 -Apply -Force -NoCode + shell: pwsh + + - name: Deploy Azure Function + if: ${{ matrix.terraform_apply }} + run: ./deploy_functions.ps1 + shell: pwsh + + - name: Test connection to Minecraft Server + if: ${{ matrix.terraform_apply }} + run: | + Set-Location (Join-Path $env:GITHUB_WORKSPACE terraform) # Task does not support 'working-directory' property + $minecraftConfig = (terraform output -json minecraft_java | ConvertFrom-Json -AsHashtable) + foreach ($minecraftConfigName in $minecraftConfig.Keys) { + Write-Host "Processing configuration '{$minecraftConfigName}''..." + $containerGroupID = $minecraftConfig[$minecraftConfigName].container_group_id + $serverFQDN = $minecraftConfig[$minecraftConfigName].minecraft_server_fqdn + $serverPort = $minecraftConfig[$minecraftConfigName].minecraft_server_port + + # Wait for Minecraft socket to open + $connectionAttempts = 0 + do { + Start-Sleep -Seconds 10 + Write-Host "Pinging ${serverFQDN} on port ${serverPort}..." + try { + $connectionAttempts++ + $mineCraftConnection = New-Object System.Net.Sockets.TcpClient($serverFQDN, $serverPort) -ErrorAction SilentlyContinue + } catch [System.Management.Automation.MethodInvocationException] { + Write-Warning $_ + } + } while ((!$mineCraftConnection || !$mineCraftConnection.Connected) -and ($connectionAttempts -le 10)) + if ($mineCraftConnection.Connected) { + Write-Host "Connected to ${serverFQDN}:${serverPort}" + $mineCraftConnection.Close() + } else { + Write-Warning "Could not connect to ${serverFQDN}:${serverPort}!" + } + + # BUG: https://github.com/Azure/azure-cli/issues/13352 + # No tty / ioctl device + # az container exec --ids $containerGroupID --exec-command "/health.sh" --container-name minecraft + script --return --quiet -c "az container exec --ids $containerGroupID --exec-command '/health.sh' --container-name minecraft" /dev/null + + az container show --ids $containerGroupID --query instanceView + az container logs --ids $containerGroupID + } + shell: pwsh + + - name: Remove backup items & resource locks + if: ${{ matrix.destroy }} + run: | + . (Join-Path $env:GITHUB_WORKSPACE scripts functions.ps1) + TearDown-Resources -Backups -Locks + shell: pwsh + + - name: Terraform Destroy + if: ${{ matrix.destroy }} + run: ./deploy.ps1 -Destroy -Force + shell: pwsh + continue-on-error: ${{ matrix.ignore_destroy_failure }} + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + if: ${{ matrix.terraform_apply }} + with: + name: terraform${{ matrix.name }} + path: "terraform" + + - name: Teardown + if: ${{ matrix.destroy || failure() }} + run: ./deploy.ps1 -Init -Teardown + shell: pwsh diff --git a/.github/workflows/ci-vanilla.yml b/.github/workflows/ci-vanilla.yml index e6fdaed..c7bd22c 100644 --- a/.github/workflows/ci-vanilla.yml +++ b/.github/workflows/ci-vanilla.yml @@ -1,29 +1,29 @@ -# Controls when the action will run. +# Controls when the action will run. on: # Triggers the workflow on push or pull request events but only for the main branch push: - branches: [ main ] + branches: [main] paths-ignore: - - '**/README.md' - - '**/LICENSE' - - 'visuals/**' + - "**/README.md" + - "**/LICENSE" + - "visuals/**" pull_request: - branches: [ main ] + branches: [main] paths-ignore: - - '**/README.md' - - '**/LICENSE' - - 'visuals/**' + - "**/README.md" + - "**/LICENSE" + - "visuals/**" schedule: - - cron: '0 3 * * Fri,Sun' + - cron: "0 3 * * Fri,Sun" # Allows you to run this workflow manually from the Actions tab workflow_dispatch: env: - AZURE_FUNCTIONAPP_PACKAGE_PATH: 'functions' - DOTNET_VERSION: '6.0.x' - FUNCTIONS_ARTIFACT_NAME: 'functions' + AZURE_FUNCTIONAPP_PACKAGE_PATH: "functions" + DOTNET_VERSION: "6.0.x" + FUNCTIONS_ARTIFACT_NAME: "functions" TF_IN_AUTOMATION: true TF_INPUT: 0 TF_VAR_location: ${{ secrets.TF_VAR_LOCATION }} @@ -38,32 +38,32 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: hashicorp/setup-terraform@v3 - - name: Setup .NET - uses: actions/setup-dotnet@v1 - with: - dotnet-version: ${{ env.DOTNET_VERSION }} - - name: Restore dependencies - run: | - pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' - dotnet restore - popd - - name: Build - run: | - pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' - dotnet build --no-restore --output ./bin/publish - popd - - name: Test - run: | - pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' - dotnet test --no-build --verbosity normal - popd - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: ${{ env.FUNCTIONS_ARTIFACT_NAME }} - path: '${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/bin/publish' + - uses: actions/checkout@v4 + - uses: hashicorp/setup-terraform@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + - name: Restore dependencies + run: | + pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' + dotnet restore + popd + - name: Build + run: | + pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' + dotnet build --no-restore --output ./bin/publish + popd + - name: Test + run: | + pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' + dotnet test --no-build --verbosity normal + popd + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ env.FUNCTIONS_ARTIFACT_NAME }} + path: "${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/bin/publish" terraform: needs: dotnet @@ -73,159 +73,159 @@ jobs: working-directory: terraform runs-on: ubuntu-latest steps: - - name: Checkout source - uses: actions/checkout@v4 - - - name: Download artifacts from previous job - uses: actions/download-artifact@v4 - with: - name: ${{ env.FUNCTIONS_ARTIFACT_NAME }} - path: '${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/bin/publish' - - - name: Use Azure CLI - uses: azure/login@v2 - with: - creds: ${{ secrets.AZURE_CREDENTIALS }} - - - name: Detect desired Terraform version - id: terraform-version-check - run: | - $terraformVersion = (Get-Content .terraform-version) - Write-Output "TERRAFORM_VERSION=${terraformVersion}" >> $env:GITHUB_OUTPUT - shell: pwsh - - name: Use Terraform - uses: hashicorp/setup-terraform@v2 - with: - terraform_version: ${{ steps.terraform-version-check.outputs.TERRAFORM_VERSION }} - terraform_wrapper: false - - - name: Terraform Init - run: | - # Parse Azure secret into Terraform variables - $servicePrincipal = ($env:AZURE_CREDENTIALS | ConvertFrom-Json) - $env:ARM_CLIENT_ID = $servicePrincipal.clientId - $env:ARM_CLIENT_SECRET = $servicePrincipal.clientSecret - $env:ARM_SUBSCRIPTION_ID = $servicePrincipal.subscriptionId - $env:ARM_TENANT_ID = $servicePrincipal.tenantId - - $env:TF_VAR_run_id=$env:GITHUB_RUN_ID - # We may not be able to create a Service Principal with a Service Principal, reuse Terraform SP for Logic App: - $env:TF_VAR_workflow_sp_application_id = $servicePrincipal.clientId - $env:TF_VAR_workflow_sp_application_secret = $servicePrincipal.clientSecret - $env:TF_VAR_workflow_sp_object_id = $servicePrincipal.objectId - - # List environment variables - Get-ChildItem -Path Env: -Recurse -Include ARM_*,AZURE_*,GITHUB_*,TF_* | Sort-Object -Property Name - # Save environment variable setup for subsequent steps - Get-ChildItem -Path Env: -Recurse -Include ARM_*,TF_VAR_* | ForEach-Object {Write-Output "$($_.Name)=$($_.Value)"} >> $env:GITHUB_ENV - - Set-Location (Join-Path $env:GITHUB_WORKSPACE terraform) # Task does not support 'working-directory' property - Get-Location - terraform init - env: - AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} - shell: pwsh - - - name: Terraform Plan - run: | - Set-Location (Join-Path $env:GITHUB_WORKSPACE terraform) # Task does not support 'working-directory' property - terraform plan -out='ci.tfplan' - shell: pwsh - - - name: Terraform Apply - if: ${{ github.event_name == 'pull_request' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} - id: terraform-apply - run: | - Set-Location (Join-Path $env:GITHUB_WORKSPACE terraform) # Task does not support 'working-directory' property - terraform apply -auto-approve 'ci.tfplan' - $exitCode = $LASTEXITCODE - if ($exitCode -ne 0) { - throw "'terraform apply' exited with status $exitCode" - } - - # Export Terraform output as step output - $terraformOutput = (terraform output -json | ConvertFrom-Json -AsHashtable) - foreach ($key in $terraformOutput.Keys) { - $outputVariableValue = $terraformOutput[$key].value - Write-Output "${key}=${outputVariableValue}" >> $env:GITHUB_OUTPUT - Write-Output "TF_OUT_${key}=${outputVariableValue}" >> $env:GITHUB_ENV - } - shell: pwsh - - - name: 'Publish Monitor Function' - if: ${{ github.event_name == 'pull_request' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} - uses: azure/functions-action@v1 - with: - app-name: ${{ steps.terraform-apply.outputs.function_name }} - package: '${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/bin/publish' - - - name: Test connection to Minecraft Server (pwsh) - if: ${{ github.event_name == 'pull_request' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} - run: | - # Wait for Minecraft to boot up - $connectionAttempts = 0 - do { - Start-Sleep -Seconds 10 - Write-Host "Pinging ${env:TF_OUT_minecraft_server_fqdn} on port ${env:TF_OUT_minecraft_server_port}..." - try { - $connectionAttempts++ - $mineCraftConnection = New-Object System.Net.Sockets.TcpClient($env:TF_OUT_minecraft_server_fqdn, $env:TF_OUT_minecraft_server_port) -ErrorAction SilentlyContinue - } catch [System.Management.Automation.MethodInvocationException] { - Write-Warning $_ + - name: Checkout source + uses: actions/checkout@v4 + + - name: Download artifacts from previous job + uses: actions/download-artifact@v4 + with: + name: ${{ env.FUNCTIONS_ARTIFACT_NAME }} + path: "${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/bin/publish" + + - name: Use Azure CLI + uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Detect desired Terraform version + id: terraform-version-check + run: | + $terraformVersion = (Get-Content .terraform-version) + Write-Output "TERRAFORM_VERSION=${terraformVersion}" >> $env:GITHUB_OUTPUT + shell: pwsh + - name: Use Terraform + uses: hashicorp/setup-terraform@v2 + with: + terraform_version: ${{ steps.terraform-version-check.outputs.TERRAFORM_VERSION }} + terraform_wrapper: false + + - name: Terraform Init + run: | + # Parse Azure secret into Terraform variables + $servicePrincipal = ($env:AZURE_CREDENTIALS | ConvertFrom-Json) + $env:ARM_CLIENT_ID = $servicePrincipal.clientId + $env:ARM_CLIENT_SECRET = $servicePrincipal.clientSecret + $env:ARM_SUBSCRIPTION_ID = $servicePrincipal.subscriptionId + $env:ARM_TENANT_ID = $servicePrincipal.tenantId + + $env:TF_VAR_run_id=$env:GITHUB_RUN_ID + # We may not be able to create a Service Principal with a Service Principal, reuse Terraform SP for Logic App: + $env:TF_VAR_workflow_sp_application_id = $servicePrincipal.clientId + $env:TF_VAR_workflow_sp_application_secret = $servicePrincipal.clientSecret + $env:TF_VAR_workflow_sp_object_id = $servicePrincipal.objectId + + # List environment variables + Get-ChildItem -Path Env: -Recurse -Include ARM_*,AZURE_*,GITHUB_*,TF_* | Sort-Object -Property Name + # Save environment variable setup for subsequent steps + Get-ChildItem -Path Env: -Recurse -Include ARM_*,TF_VAR_* | ForEach-Object {Write-Output "$($_.Name)=$($_.Value)"} >> $env:GITHUB_ENV + + Set-Location (Join-Path $env:GITHUB_WORKSPACE terraform) # Task does not support 'working-directory' property + Get-Location + terraform init + env: + AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} + shell: pwsh + + - name: Terraform Plan + run: | + Set-Location (Join-Path $env:GITHUB_WORKSPACE terraform) # Task does not support 'working-directory' property + terraform plan -out='ci.tfplan' + shell: pwsh + + - name: Terraform Apply + if: ${{ github.event_name == 'pull_request' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} + id: terraform-apply + run: | + Set-Location (Join-Path $env:GITHUB_WORKSPACE terraform) # Task does not support 'working-directory' property + terraform apply -auto-approve 'ci.tfplan' + $exitCode = $LASTEXITCODE + if ($exitCode -ne 0) { + throw "'terraform apply' exited with status $exitCode" + } + + # Export Terraform output as step output + $terraformOutput = (terraform output -json | ConvertFrom-Json -AsHashtable) + foreach ($key in $terraformOutput.Keys) { + $outputVariableValue = $terraformOutput[$key].value + Write-Output "${key}=${outputVariableValue}" >> $env:GITHUB_OUTPUT + Write-Output "TF_OUT_${key}=${outputVariableValue}" >> $env:GITHUB_ENV } - } while ((!$mineCraftConnection || !$mineCraftConnection.Connected) -and ($connectionAttempts -le 10)) - - # No tty - # az container exec --ids ${{ steps.terraform-apply.outputs.container_group_id }} --exec-command "/health.sh" --container-name minecraft - env: - AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} - shell: pwsh - - # - name: Test connection to Minecraft Server (bash) - # if: ${{ github.event_name == 'pull_request' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} - # run: | - # # Wait for Minecraft to boot up - # while ! echo exit | nc $TF_OUT_minecraft_server_fqdn $TF_OUT_minecraft_server_port; do sleep 10; done - # # No tty - # # az container exec --ids ${{ steps.terraform-apply.outputs.container_group_id }} --exec-command "/health.sh" --container-name minecraft - # shell: bash - - - name: Terraform Destroy - if: ${{ github.event_name == 'pull_request' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} - run: | - Set-Location (Join-Path $env:GITHUB_WORKSPACE terraform) # Task does not support 'working-directory' property - terraform destroy -auto-approve - shell: pwsh - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: terraform - path: 'terraform' - - - name: Clean Up - if: ${{ always() }} - run: | - $ErrorActionPreference = "Continue" - Set-Location (Join-Path $env:GITHUB_WORKSPACE terraform) # Task does not support 'working-directory' property - - # Remove resource locks first - $resourceLocksJSON = $(terraform output -json resource_locks 2>$null) - if ($resourceLocksJSON -and ($resourceLocksJSON -match "^\[.*\]$")) { - $resourceLocks = ($resourceLocksJSON | ConvertFrom-JSON) - az resource lock delete --ids $resourceLocks --verbose - } - - # Build JMESPath expression - $repository = ($env:GITHUB_REPOSITORY).Split("/")[-1] - $tagQuery = "[?tags.repository == '${repository}' && tags.workspace == '${env:TF_WORKSPACE}' && tags.runid == '${env:GITHUB_RUN_ID}' && properties.provisioningState != 'Deleting'].id" - - Write-Host "Removing resource group identified by `"$tagQuery`"..." - $resourceGroupIDs = $(az group list --query "$tagQuery" -o tsv) - if ($resourceGroupIDs) { - Write-Host "az resource delete --ids ${resourceGroupIDs}..." - az resource delete --ids $resourceGroupIDs --verbose - } else { - Write-Host "Nothing to remove" - } - shell: pwsh + shell: pwsh + + - name: "Publish Monitor Function" + if: ${{ github.event_name == 'pull_request' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} + uses: azure/functions-action@v1 + with: + app-name: ${{ steps.terraform-apply.outputs.function_name }} + package: "${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/bin/publish" + + - name: Test connection to Minecraft Server (pwsh) + if: ${{ github.event_name == 'pull_request' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} + run: | + # Wait for Minecraft to boot up + $connectionAttempts = 0 + do { + Start-Sleep -Seconds 10 + Write-Host "Pinging ${env:TF_OUT_minecraft_server_fqdn} on port ${env:TF_OUT_minecraft_server_port}..." + try { + $connectionAttempts++ + $mineCraftConnection = New-Object System.Net.Sockets.TcpClient($env:TF_OUT_minecraft_server_fqdn, $env:TF_OUT_minecraft_server_port) -ErrorAction SilentlyContinue + } catch [System.Management.Automation.MethodInvocationException] { + Write-Warning $_ + } + } while ((!$mineCraftConnection || !$mineCraftConnection.Connected) -and ($connectionAttempts -le 10)) + + # No tty + # az container exec --ids ${{ steps.terraform-apply.outputs.container_group_id }} --exec-command "/health.sh" --container-name minecraft + env: + AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} + shell: pwsh + + # - name: Test connection to Minecraft Server (bash) + # if: ${{ github.event_name == 'pull_request' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} + # run: | + # # Wait for Minecraft to boot up + # while ! echo exit | nc $TF_OUT_minecraft_server_fqdn $TF_OUT_minecraft_server_port; do sleep 10; done + # # No tty + # # az container exec --ids ${{ steps.terraform-apply.outputs.container_group_id }} --exec-command "/health.sh" --container-name minecraft + # shell: bash + + - name: Terraform Destroy + if: ${{ github.event_name == 'pull_request' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} + run: | + Set-Location (Join-Path $env:GITHUB_WORKSPACE terraform) # Task does not support 'working-directory' property + terraform destroy -auto-approve + shell: pwsh + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: terraform + path: "terraform" + + - name: Clean Up + if: ${{ always() }} + run: | + $ErrorActionPreference = "Continue" + Set-Location (Join-Path $env:GITHUB_WORKSPACE terraform) # Task does not support 'working-directory' property + + # Remove resource locks first + $resourceLocksJSON = $(terraform output -json resource_locks 2>$null) + if ($resourceLocksJSON -and ($resourceLocksJSON -match "^\[.*\]$")) { + $resourceLocks = ($resourceLocksJSON | ConvertFrom-JSON) + az resource lock delete --ids $resourceLocks --verbose + } + + # Build JMESPath expression + $repository = ($env:GITHUB_REPOSITORY).Split("/")[-1] + $tagQuery = "[?tags.repository == '${repository}' && tags.workspace == '${env:TF_WORKSPACE}' && tags.runid == '${env:GITHUB_RUN_ID}' && properties.provisioningState != 'Deleting'].id" + + Write-Host "Removing resource group identified by `"$tagQuery`"..." + $resourceGroupIDs = $(az group list --query "$tagQuery" -o tsv) + if ($resourceGroupIDs) { + Write-Host "az resource delete --ids ${resourceGroupIDs}..." + az resource delete --ids $resourceGroupIDs --verbose + } else { + Write-Host "Nothing to remove" + } + shell: pwsh diff --git a/.github/workflows/test.json b/.github/workflows/test.json index f3769d1..c684010 100644 --- a/.github/workflows/test.json +++ b/.github/workflows/test.json @@ -1,11 +1,6 @@ { - "os": [ - "ubuntu-latest" - ], - "use_latest_versions": [ - false, - true - ], + "os": ["ubuntu-latest"], + "use_latest_versions": [false, true], "include": [ { "use_latest_versions": false, @@ -24,4 +19,4 @@ "upgrade_azure_cli": true } ] -} \ No newline at end of file +} diff --git a/Brewfile b/Brewfile index 592678f..04bfcfb 100644 --- a/Brewfile +++ b/Brewfile @@ -1,12 +1,10 @@ tap "azure/functions" -tap "isen-ng/dotnet-sdk-versions" brew "azure-cli" -brew "azure-functions-core-tools@3" +brew "azure-functions-core-tools@4" brew "hashicorp/tap/terraform" brew "jq" brew "tfenv" -# cask "dotnet-sdk6" -cask "dotnet-sdk6-0-400" +cask "dotnet-sdk" cask "powershell" \ No newline at end of file diff --git a/README.md b/README.md index ac3ce8c..cc78597 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Minecraft Server powered by Docker & Azure Container Instance + This repo deploys [itzg/minecraft-server](https://hub.docker.com/r/itzg/minecraft-server) as [Azure Container Instance](https://azure.microsoft.com/en-us/services/container-instances/), using Terraform. [![ci-scripted](https://github.com/geekzter/azure-minecraft-docker/actions/workflows/ci-scripted.yml/badge.svg)](https://github.com/geekzter/azure-minecraft-docker/actions/workflows/ci-scripted.yml) @@ -8,19 +9,26 @@ This repo deploys [itzg/minecraft-server](https://hub.docker.com/r/itzg/minecraf ![alt text](./visuals/diagram.png "Diagram") ## Instructions + There are 2 ways to set this up: ### Codespace setup + The easiest method is to use a GitHub [Codespace](https://github.com/features/codespaces) (in beta). Just create a GitHub Codespace from the Code menu. Wait for the Codespace to complete provisioning. When the Codespace has completed provisioning and you open a terminal window (Ctrl-`, Control-backquote), you should see a message like this: + ``` To provision infrastructure, make sure you're logged in with Azure CLI e.g. run 'az login' and 'az account set --subscription 00000000-0000-0000-0000-000000000000'. Then, either: - change to the /home/codespace/workspace/azure-minecraft-docker/terraform directory and run 'terraform apply', or: - run /home/codespace/workspace/azure-minecraft-docker/scripts/deploy.ps1 -apply To destroy infrastructure, replace 'apply' with 'destroy' in above commands ``` + Just follow these steps to provision Minecraft on Azure. + ### Local setup + If you set this up locally, make sure you have the following pre-requisites: + - [Azure CLI](http://aka.ms/azure-cli) - [PowerShell](https://github.com/PowerShell/PowerShell#get-powershell) - [Terraform](https://www.terraform.io/downloads.html) (to get that you can use [tfenv](https://github.com/tfutils/tfenv) on Linux & macOS, [Homebrew](https://github.com/hashicorp/homebrew-tap) on macOS or [chocolatey](https://chocolatey.org/packages/terraform) on Windows) @@ -28,13 +36,16 @@ If you set this up locally, make sure you have the following pre-requisites: On macOS, you can run `brew bundle` to install the pre-requisites. Once you have those, you can go ahead and provision: + - Use Azure CLI for SSO with [Terraform](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/azure_cli): `az login` - Select subscription to use: `az account set --subscription 00000000-0000-0000-0000-000000000000` - Initialize terraform: `terraform init` - Provision cloud infrastructure: `terraform apply` ### Customization + You can customize the deployment by overriding defaults for Terraform [input variables](https://www.terraform.io/docs/configuration/variables.html). The easiest way to do this is to copy [config.auto.example.tfvars](./terraform/config.auto.example.tfvars) and save it as config.auto.tfvars. + - Use the `minecraft_users` array to define users allowed to log in - Use a custom DNS name with `vanity_dns_zone_id` and `vanity_hostname_prefix`, using an Azure DNS managed domain - Once things get serious, you may want to start backing up data with `enable_backup` @@ -42,14 +53,17 @@ You can customize the deployment by overriding defaults for Terraform [input var - Concerned about chat messages appearing in logs? `enable_log_filter` uses the [Console Spam Fix](https://dev.bukkit.org/projects/console-spam-fix) and [configures](./minecraft/log-filter/config.yml) it to hide chat messages from logs. This feature is dependent on [PowerShell](https://github.com/PowerShell/PowerShell) and is more brittle in automation, but satisfies a key privacy requirement. See [variables.tf](./terraform/variables.tf) for all input variables. + ## Dashboard -Once provisioned, a dashboard like this will be available in the Azure Portal: + +Once provisioned, a dashboard like this will be available in the Azure Portal: ![alt text](./visuals/dashboard.png "Dashboard") You can update the dashboard in the portal and re-generate the [template](./terraform/dashboard.tpl) using [templatize_dashboard.ps1](./scripts/templatize_dashboard.ps1). ## Resources + - [itzg/minecraft-server](https://hub.docker.com/r/itzg/minecraft-server) on Docker Hub - [docker-minecraft-server](https://github.com/itzg/docker-minecraft-server) on Github - [Minecraft on Azure Friday](https://www.youtube.com/watch?v=2D8FTi-Zvt0) (uses Docker CLI workflow) -- [Minecraft on Docker Blog](https://www.docker.com/blog/deploying-a-minecraft-docker-server-to-the-cloud/) (uses Docker CLI workflow) \ No newline at end of file +- [Minecraft on Docker Blog](https://www.docker.com/blog/deploying-a-minecraft-docker-server-to-the-cloud/) (uses Docker CLI workflow) diff --git a/azure-minecraft-docker.sln b/azure-minecraft-docker.sln new file mode 100644 index 0000000..1d7edcf --- /dev/null +++ b/azure-minecraft-docker.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "functions", "functions\functions.csproj", "{864DEE18-2785-48AE-AA34-EAF7C183EADA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {864DEE18-2785-48AE-AA34-EAF7C183EADA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {864DEE18-2785-48AE-AA34-EAF7C183EADA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {864DEE18-2785-48AE-AA34-EAF7C183EADA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {864DEE18-2785-48AE-AA34-EAF7C183EADA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {943984F9-29C4-4669-9565-E56B8B6BF142} + EndGlobalSection +EndGlobal diff --git a/functions/functions.csproj b/functions/functions.csproj index fad39f2..8ae7540 100644 --- a/functions/functions.csproj +++ b/functions/functions.csproj @@ -1,11 +1,11 @@ - net6.0 + net8.0 v4 - - + + diff --git a/functions/global.json b/functions/global.json deleted file mode 100644 index d832153..0000000 --- a/functions/global.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "sdk": { - "version": "6.0" - } -} \ No newline at end of file diff --git a/functions/host.json b/functions/host.json index bb3b8da..6a0819c 100644 --- a/functions/host.json +++ b/functions/host.json @@ -1,11 +1,11 @@ { - "version": "2.0", - "logging": { - "applicationInsights": { - "samplingExcludedTypes": "Request", - "samplingSettings": { - "isEnabled": true - } - } + "version": "2.0", + "logging": { + "applicationInsights": { + "samplingExcludedTypes": "Request", + "samplingSettings": { + "isEnabled": true + } } -} \ No newline at end of file + } +} diff --git a/global.json b/global.json new file mode 100644 index 0000000..391ba3c --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "8.0.100", + "rollForward": "latestFeature" + } +} diff --git a/minecraft/bstats/config.yml b/minecraft/bstats/config.yml index 2d58242..2cc118e 100644 --- a/minecraft/bstats/config.yml +++ b/minecraft/bstats/config.yml @@ -1,4 +1,4 @@ # Hode stats enabled: false serverUuid: 00000000-0000-0000-0000-000000000000 -logFailedRequests: false \ No newline at end of file +logFailedRequests: false diff --git a/minecraft/log-filter/config.yml b/minecraft/log-filter/config.yml index e33104e..8f9dd69 100644 --- a/minecraft/log-filter/config.yml +++ b/minecraft/log-filter/config.yml @@ -3,13 +3,13 @@ # https://www.spigotmc.org/resources/console-spam-fix.18410 Messages-To-Hide-Filter: -# Chat messages from users -- '<' -- '>' + # Chat messages from users + - "<" + - ">" #Chat messages with color and format codes support. -#Minecraft Color and Format Codes: http://minecraft.gamepedia.com/Formatting_codes +#Minecraft Color and Format Codes: http://minecraft.gamepedia.com/Formatting_codes ChatMessages: - NoPermission: '&4[System] &cYou don''t have permission!' - CmdHelp: '&4[System] &cAvailable commands:&6 /csf reload' - CmdReload: '&2[System] &aConfig reload complete!' \ No newline at end of file + NoPermission: "&4[System] &cYou don't have permission!" + CmdHelp: "&4[System] &cAvailable commands:&6 /csf reload" + CmdReload: "&2[System] &aConfig reload complete!" diff --git a/multi-instance.md b/multi-instance.md index ca48ed1..aadb145 100644 --- a/multi-instance.md +++ b/multi-instance.md @@ -1,18 +1,23 @@ # Multi Instance -The Terraform code has been restructured to allow for multiple Minecraft instances e.g. 1.6.5 & 1.7, or different distributions. To allow this, a lot of resources have moved into Terraform modules. This includes Azure file shares, that have state (Minecraft world data). + +The Terraform code has been restructured to allow for multiple Minecraft instances e.g. 1.6.5 & 1.7, or different distributions. To allow this, a lot of resources have moved into Terraform modules. This includes Azure file shares, that have state (Minecraft world data). To reconcile Terraform state with the actual resources (without losing data!), there are 2 approaches: ## 1 - Scripted deployment + In the case Minecraft is deployed with [deploy.ps1](scripts/deploy.ps1), the script will take care of moving file shares within Terraform state prior to plan stage. The script will prompt for confirmation, as custom configuration applied e.g. via an [.auto.tfvars file](terraform/config.auto.example.tfvars) needs to be applied differently as [variables.tf](terraform/variables.tf) has changed significantly. You can also migrate file shares in Terraform state running [migrate_storage_share_state.ps1](scripts/migrate_storage_share_state.ps1) prior to running deploy.ps1. ## 2 - Plain 'terraform apply' + If Minecraft is deployed without using deploy.ps1, Terraform will attempt to destroy and recreate resources. In the case of file shares, which have purge protection enabled, nothing will get destroyed (rather flagged as deleted in a restorable state) and Terraform will run into an existing resource. These can than be restored and imported with [import_file_shares.ps1](scripts/import_file_shares.ps1). ## Known issues -There is a known [issue](https://github.com/terraform-providers/terraform-provider-azurerm/issues/11184#issuecomment-870535683) with `azurerm_backup_protected_file_share` where an error is thrown under certain conditions: + +There is a known [issue](https://github.com/terraform-providers/terraform-provider-azurerm/issues/11184#issuecomment-870535683) with `azurerm_backup_protected_file_share` where an error is thrown under certain conditions: + ``` [ERROR] fileshare 'minecraft-aci-experimental-data-xxxx' not found in protectable or protected fileshares, make sure Storage Account "minecraftstorxxxx" is registered with Recovery Service Vault "Minecraft-default-xxxx-backup" (Resource Group "Minecraft-default-xxxx") ``` -If this happens, running [manage_unprotected_shares.ps1](scripts/manage_unprotected_shares.ps1) with the `-ToggleUnprotectedItemState` switch will resolve the issue. \ No newline at end of file +If this happens, running [manage_unprotected_shares.ps1](scripts/manage_unprotected_shares.ps1) with the `-ToggleUnprotectedItemState` switch will resolve the issue. diff --git a/terraform/arm/startstop-template.json b/terraform/arm/startstop-template.json index a662172..5636e5d 100644 --- a/terraform/arm/startstop-template.json +++ b/terraform/arm/startstop-template.json @@ -1,59 +1,58 @@ { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - }, - "variables": {}, - "resources": [ - { - "type": "Microsoft.Logic/workflows", - "apiVersion": "2017-07-01", - "name": "${workflow_name}", - "location": "${location}", - "properties": { - "state": "Enabled", - "definition": { - "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "$connections": { - "defaultValue": {}, - "type": "Object" - } - }, - "triggers": {}, - "actions": { - "${operation}_minecraft": { - "runAfter": {}, - "type": "ApiConnection", - "inputs": { - "host": { - "connection": { - "name": "@parameters('$connections')['${connection_name_json}']['connectionId']" - } - }, - "method": "post", - "path": "${container_group_id}/${operation}", - "queries": { - "x-ms-api-version": "2019-12-01" - } - } - } - }, - "outputs": {} + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2017-07-01", + "name": "${workflow_name}", + "location": "${location}", + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "$connections": { + "defaultValue": {}, + "type": "Object" + } + }, + "triggers": {}, + "actions": { + "${operation}_minecraft": { + "runAfter": {}, + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['${connection_name_json}']['connectionId']" + } }, - "parameters": { - "$connections": { - "value": { - "${connection_name_json}": { - "connectionId": "${connection_id}", - "connectionName": "${connection_name}", - "id": "${api_id}" - } - } - } + "method": "post", + "path": "${container_group_id}/${operation}", + "queries": { + "x-ms-api-version": "2019-12-01" } + } + } + }, + "outputs": {} + }, + "parameters": { + "$connections": { + "value": { + "${connection_name_json}": { + "connectionId": "${connection_id}", + "connectionName": "${connection_name}", + "id": "${api_id}" + } } + } } - ] -} \ No newline at end of file + } + } + ] +} diff --git a/terraform/arm/workflow-connection-template.json b/terraform/arm/workflow-connection-template.json index e2a9af2..6db402d 100644 --- a/terraform/arm/workflow-connection-template.json +++ b/terraform/arm/workflow-connection-template.json @@ -1,33 +1,34 @@ { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - }, - "resources": [{ - "type": "Microsoft.Web/connections", - "name": "${connection_name}", - "apiVersion": "2016-06-01", - "location": "${location}", - "scale": null, - "properties": { - "displayName": "${connection_display_name}", - "api": { - "type": "Microsoft.Web/locations/managedApis", - "id": "${api_id}", - "name": "aci", - "displayName": "Azure Container Instance", - "description": "Easily run containers on Azure with a single command. Create container groups, get the logs of a container and more.", - "iconUri": "https://connectoricons-prod.azureedge.net/releases/v1.0.1385/1.0.1385.2110/aci/icon.png", - "brandColor": "#0089D0", - "category": "Standard" - }, - "parameterValues": { - "token:clientId": "${client_id}", - "token:clientSecret": "${client_secret}", - "token:tenantId": "${tenant_id}", - "token:grantType": "client_credentials" - } + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": {}, + "resources": [ + { + "type": "Microsoft.Web/connections", + "name": "${connection_name}", + "apiVersion": "2016-06-01", + "location": "${location}", + "scale": null, + "properties": { + "displayName": "${connection_display_name}", + "api": { + "type": "Microsoft.Web/locations/managedApis", + "id": "${api_id}", + "name": "aci", + "displayName": "Azure Container Instance", + "description": "Easily run containers on Azure with a single command. Create container groups, get the logs of a container and more.", + "iconUri": "https://connectoricons-prod.azureedge.net/releases/v1.0.1385/1.0.1385.2110/aci/icon.png", + "brandColor": "#0089D0", + "category": "Standard" }, - "dependsOn": [] - }] -} \ No newline at end of file + "parameterValues": { + "token:clientId": "${client_id}", + "token:clientSecret": "${client_secret}", + "token:tenantId": "${tenant_id}", + "token:grantType": "client_credentials" + } + }, + "dependsOn": [] + } + ] +}