HydroServer AWS Cloud Deployment #125
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: HydroServer AWS Cloud Deployment | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| environment: | |
| description: 'Enter a deployment environment to use.' | |
| type: environment | |
| required: true | |
| action: | |
| description: 'Enter the action you want to perform.' | |
| type: choice | |
| required: true | |
| options: | |
| - Initialize HydroServer Deployment | |
| - Update HydroServer Deployment | |
| - Teardown HydroServer Deployment | |
| release: | |
| description: 'Enter the HydroServer release tag to use.' | |
| type: string | |
| required: false | |
| default: 'latest' | |
| permissions: | |
| contents: write | |
| id-token: write | |
| actions: write | |
| jobs: | |
| check-environment-variables: | |
| runs-on: ubuntu-latest | |
| environment: ${{ github.event.inputs.environment }} | |
| env: | |
| AWS_ACCOUNT_ID: ${{ vars.AWS_ACCOUNT_ID }} | |
| AWS_IAM_ROLE: ${{ vars.AWS_IAM_ROLE }} | |
| AWS_REGION: ${{ vars.AWS_REGION }} | |
| AWS_ACM_CERTIFICATE_ARN: ${{ vars.AWS_ACM_CERTIFICATE_ARN }} | |
| PROXY_BASE_URL: ${{ vars.PROXY_BASE_URL }} | |
| TERRAFORM_BUCKET: ${{ vars.TERRAFORM_BUCKET }} | |
| steps: | |
| - name: Check Required Environment Variables | |
| run: | | |
| echo "Checking required environment variables..." | |
| required_vars=(AWS_ACCOUNT_ID AWS_IAM_ROLE AWS_REGION AWS_ACM_CERTIFICATE_ARN PROXY_BASE_URL TERRAFORM_BUCKET) | |
| for var in "${required_vars[@]}"; do | |
| if [ -z "${!var}" ]; then | |
| echo "Error: Environment variable $var is not defined." | |
| exit 1 | |
| fi | |
| done | |
| initialize-hydroserver: | |
| needs: check-environment-variables | |
| if: github.event.inputs.action == 'Initialize HydroServer Deployment' | |
| runs-on: ubuntu-latest | |
| environment: ${{ github.event.inputs.environment }} | |
| defaults: | |
| run: | |
| working-directory: ./terraform/aws | |
| steps: | |
| - name: Checkout Ops Repo | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.ref }} | |
| - name: Configure AWS Credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/${{ vars.AWS_IAM_ROLE }} | |
| role-session-name: create-hydroserver-resources | |
| aws-region: ${{ vars.AWS_REGION }} | |
| - name: Setup Terraform | |
| uses: hashicorp/setup-terraform@v1 | |
| - name: Terraform Init | |
| run: | | |
| terraform init \ | |
| -backend-config="bucket=${{ vars.TERRAFORM_BUCKET }}" \ | |
| -backend-config="region=${{ vars.AWS_REGION }}" \ | |
| -backend-config="key=state/hydroserver_${{ github.event.inputs.environment }}" | |
| - name: Terraform Plan | |
| id: plan | |
| run: | | |
| terraform plan \ | |
| -no-color -input=false \ | |
| -var "instance=${{ github.event.inputs.environment }}" \ | |
| -var "region=${{ vars.AWS_REGION }}" \ | |
| -var "proxy_base_url=${{ vars.PROXY_BASE_URL }}" \ | |
| -var "acm_certificate_arn=${{ vars.AWS_ACM_CERTIFICATE_ARN }}" \ | |
| -var "database_url=${{ secrets.DATABASE_URL || '' }}" \ | |
| -var "tag_key=${{ vars.AWS_TAG_KEY || 'hydroserver-instance' }}" \ | |
| -var "tag_value=${{ vars.AWS_TAG_VALUE || github.event.inputs.environment }}" | |
| continue-on-error: true | |
| - name: Terraform Plan Status | |
| if: steps.plan.outcome == 'failure' | |
| run: exit 1 | |
| - name: Terraform Apply ECR | |
| run: | | |
| terraform apply \ | |
| -auto-approve -input=false \ | |
| -target=aws_ecr_repository.api_repository \ | |
| -var "instance=${{ github.event.inputs.environment }}" \ | |
| -var "region=${{ vars.AWS_REGION }}" \ | |
| -var "proxy_base_url=${{ vars.PROXY_BASE_URL }}" \ | |
| -var "acm_certificate_arn=${{ vars.AWS_ACM_CERTIFICATE_ARN }}" \ | |
| -var "database_url=${{ secrets.DATABASE_URL || '' }}" \ | |
| -var "tag_key=${{ vars.AWS_TAG_KEY || 'hydroserver-instance' }}" \ | |
| -var "tag_value=${{ vars.AWS_TAG_VALUE || github.event.inputs.environment }}" | |
| - name: Log in to AWS ECR | |
| run: | | |
| aws ecr get-login-password --region ${{ vars.AWS_REGION }} | docker login --username AWS --password-stdin "${{ vars.AWS_ACCOUNT_ID }}.dkr.ecr.${{ vars.AWS_REGION }}.amazonaws.com" | |
| - name: Pull image from GHCR | |
| run: | | |
| docker pull ghcr.io/hydroserver2/hydroserver-api-services:latest | |
| - name: Tag Docker Image for ECR | |
| run: | | |
| docker tag ghcr.io/hydroserver2/hydroserver-api-services:latest \ | |
| ${{ vars.AWS_ACCOUNT_ID }}.dkr.ecr.${{ vars.AWS_REGION }}.amazonaws.com/hydroserver-api-${{ github.event.inputs.environment }}:latest | |
| - name: Push Docker Image to AWS ECR | |
| run: | | |
| docker push ${{ vars.AWS_ACCOUNT_ID }}.dkr.ecr.${{ vars.AWS_REGION }}.amazonaws.com/hydroserver-api-${{ github.event.inputs.environment }}:latest | |
| - name: Terraform Apply | |
| run: | | |
| terraform apply \ | |
| -auto-approve -input=false \ | |
| -var "instance=${{ github.event.inputs.environment }}" \ | |
| -var "region=${{ vars.AWS_REGION }}" \ | |
| -var "proxy_base_url=${{ vars.PROXY_BASE_URL }}" \ | |
| -var "acm_certificate_arn=${{ vars.AWS_ACM_CERTIFICATE_ARN }}" \ | |
| -var "database_url=${{ secrets.DATABASE_URL || '' }}" \ | |
| -var "tag_key=${{ vars.AWS_TAG_KEY || 'hydroserver-instance' }}" \ | |
| -var "tag_value=${{ vars.AWS_TAG_VALUE || github.event.inputs.environment }}" | |
| deploy-hydroserver: | |
| needs: | |
| - check-environment-variables | |
| - initialize-hydroserver | |
| if: > | |
| always() && | |
| !contains(needs.*.result, 'failure') && | |
| ( | |
| (github.event.inputs.action == 'Initialize HydroServer Deployment' && needs.initialize-hydroserver.result == 'success') || | |
| (github.event.inputs.action == 'Update HydroServer Deployment' && needs.check-environment-variables.result == 'success') | |
| ) | |
| runs-on: ubuntu-latest | |
| environment: ${{ github.event.inputs.environment }} | |
| defaults: | |
| run: | |
| working-directory: ./terraform/aws | |
| steps: | |
| - name: Checkout Ops Repo | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.ref }} | |
| - name: Configure AWS Credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/${{ vars.AWS_IAM_ROLE }} | |
| role-session-name: create-hydroserver-resources | |
| aws-region: ${{ vars.AWS_REGION }} | |
| - name: Log in to AWS ECR | |
| run: | | |
| aws ecr get-login-password --region ${{ vars.AWS_REGION }} | docker login --username AWS --password-stdin "${{ vars.AWS_ACCOUNT_ID }}.dkr.ecr.${{ vars.AWS_REGION }}.amazonaws.com" | |
| - name: Pull image from GHCR | |
| run: | | |
| docker pull ghcr.io/hydroserver2/hydroserver-api-services:${{ github.event.inputs.release }} | |
| - name: Tag Docker Image for ECR | |
| run: | | |
| docker tag ghcr.io/hydroserver2/hydroserver-api-services:${{ github.event.inputs.release }} \ | |
| ${{ vars.AWS_ACCOUNT_ID }}.dkr.ecr.${{ vars.AWS_REGION }}.amazonaws.com/hydroserver-api-${{ github.event.inputs.environment }}:latest | |
| - name: Push Docker Image to AWS ECR | |
| run: | | |
| docker push ${{ vars.AWS_ACCOUNT_ID }}.dkr.ecr.${{ vars.AWS_REGION }}.amazonaws.com/hydroserver-api-${{ github.event.inputs.environment }}:latest | |
| - name: Update AWS SSM Parameters | |
| run: | | |
| ENVIRONMENT="${{ github.event.inputs.environment }}" | |
| declare -A PARAM_TYPES=( | |
| ["/hydroserver-${ENVIRONMENT}-api/database-url"]="SecureString" | |
| ["/hydroserver-${ENVIRONMENT}-api/secret-key"]="SecureString" | |
| ["/hydroserver-${ENVIRONMENT}-api/smtp-url"]="SecureString" | |
| ["/hydroserver-${ENVIRONMENT}-api/debug-mode"]="String" | |
| ["/hydroserver-${ENVIRONMENT}-api/proxy-base-url"]="String" | |
| ["/hydroserver-${ENVIRONMENT}-api/account-ownership-enabled"]="String" | |
| ["/hydroserver-${ENVIRONMENT}-api/account-signup-enabled"]="String" | |
| ["/hydroserver-${ENVIRONMENT}-api/socialaccount-signup-only"]="String" | |
| ) | |
| declare -A PARAM_VALUES=( | |
| ["/hydroserver-${ENVIRONMENT}-api/database-url"]="${{ secrets.DATABASE_URL }}" | |
| ["/hydroserver-${ENVIRONMENT}-api/secret-key"]="${{ secrets.SECRET_KEY }}" | |
| ["/hydroserver-${ENVIRONMENT}-api/smtp-url"]="${{ secrets.SMTP_URL }}" | |
| ["/hydroserver-${ENVIRONMENT}-api/debug-mode"]="${{ vars.DEBUG }}" | |
| ["/hydroserver-${ENVIRONMENT}-api/proxy-base-url"]="${{ vars.PROXY_BASE_URL }}" | |
| ["/hydroserver-${ENVIRONMENT}-api/account-ownership-enabled"]="${{ vars.ACCOUNT_OWNERSHIP_ENABLED }}" | |
| ["/hydroserver-${ENVIRONMENT}-api/account-signup-enabled"]="${{ vars.ACCOUNT_SIGNUP_ENABLED }}" | |
| ["/hydroserver-${ENVIRONMENT}-api/socialaccount-signup-only"]="${{ vars.SOCIALACCOUNT_SIGNUP_ONLY }}" | |
| ) | |
| for PARAM in "${!PARAM_VALUES[@]}"; do | |
| VALUE="${PARAM_VALUES[$PARAM]}" | |
| TYPE="${PARAM_TYPES[$PARAM]}" | |
| if [ -n "$VALUE" ]; then | |
| ESCAPED_VALUE=$(printf "%s" "$VALUE" | jq -Rs . | sed 's/^"\(.*\)"$/\1/') | |
| echo "Updating $PARAM in AWS SSM Parameter Store..." | |
| aws ssm put-parameter --name "$PARAM" --value "$ESCAPED_VALUE" --type "$TYPE" --overwrite | |
| else | |
| echo "Skipping $PARAM as it is not defined." | |
| fi | |
| done | |
| - name: Run HydroServer Setup Commands | |
| run: | | |
| LOAD_DEFAULT_DATA="${{ vars.LOAD_DEFAULT_DATA || 'False' }}" | |
| aws batch submit-job \ | |
| --job-name hydroserver-init-${{ github.event.inputs.environment }} \ | |
| --job-queue hydroserver-init-${{ github.event.inputs.environment }} \ | |
| --job-definition hydroserver-init-${{ github.event.inputs.environment }} \ | |
| --container-overrides "{\"environment\":[{\"name\":\"LOAD_DEFAULT_DATA\",\"value\":\"$LOAD_DEFAULT_DATA\"}]}" | |
| - name: Deploy AWS App Runner Service | |
| run: | | |
| APP_RUNNER_SERVICE_NAME="hydroserver-api-${{ github.event.inputs.environment }}" | |
| SERVICE_ARN=$(aws apprunner list-services --query "ServiceSummaryList[?ServiceName=='$APP_RUNNER_SERVICE_NAME'].ServiceArn" --output text) | |
| aws apprunner start-deployment --service-arn "$SERVICE_ARN" | |
| - name: Get CloudFront Distribution ID | |
| run: | | |
| DISTRIBUTION_IDS=$(aws cloudfront list-distributions --query "DistributionList.Items[*].Id" --output text) | |
| for ID in $DISTRIBUTION_IDS; do | |
| TAGS_JSON=$(aws cloudfront list-tags-for-resource --resource "arn:aws:cloudfront::${{ vars.AWS_ACCOUNT_ID }}:distribution/$ID") | |
| MATCHING_ID=$(echo "$TAGS_JSON" | jq -r ' | |
| if any(.Tags.Items[]; .Key == "'"${{ vars.AWS_TAG_KEY || 'hydroserver-instance' }}"'" and .Value == "'"${{ vars.AWS_TAG_VALUE || github.event.inputs.environment }}"'") then "'$ID'" else empty end | |
| ') | |
| if [ -n "$MATCHING_ID" ]; then | |
| CLOUDFRONT_ID=$MATCHING_ID | |
| break | |
| fi | |
| done | |
| echo "CLOUDFRONT_ID=$CLOUDFRONT_ID" >> $GITHUB_ENV | |
| - name: Invalidate CloudFront Distribution Cache | |
| run: | | |
| aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_ID --paths "/*" | |
| teardown-hydroserver: | |
| needs: check-environment-variables | |
| runs-on: ubuntu-latest | |
| environment: ${{ github.event.inputs.environment }} | |
| if: success() && github.event.inputs.action == 'Teardown HydroServer Deployment' | |
| defaults: | |
| run: | |
| working-directory: ./terraform/aws | |
| steps: | |
| - name: Checkout Ops Repo | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.ref }} | |
| - name: Configure AWS Credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/${{ vars.AWS_IAM_ROLE }} | |
| role-session-name: create-hydroserver-resources | |
| aws-region: ${{ vars.AWS_REGION }} | |
| - name: Setup Terraform | |
| uses: hashicorp/setup-terraform@v1 | |
| - name: Terraform Init | |
| run: | | |
| terraform init \ | |
| -backend-config="bucket=${{ vars.TERRAFORM_BUCKET }}" \ | |
| -backend-config="region=${{ vars.AWS_REGION }}" \ | |
| -backend-config="key=state/hydroserver_${{ github.event.inputs.environment }}" | |
| - name: Terraform Plan Destroy | |
| id: plan_destroy | |
| run: | | |
| terraform state rm aws_db_instance.rds_db_instance || true | |
| terraform plan -destroy -no-color -input=false \ | |
| -var "instance=${{ github.event.inputs.environment }}" \ | |
| -var "region=${{ vars.AWS_REGION }}" \ | |
| -var "proxy_base_url=${{ vars.PROXY_BASE_URL }}" \ | |
| -var "acm_certificate_arn=${{ vars.AWS_ACM_CERTIFICATE_ARN }}" | |
| continue-on-error: true | |
| - name: Terraform Plan Destroy Status | |
| if: steps.plan_destroy.outcome == 'failure' | |
| run: exit 1 | |
| - name: Terraform Destroy | |
| run: | | |
| terraform destroy -auto-approve -input=false \ | |
| -var "instance=${{ github.event.inputs.environment }}" \ | |
| -var "region=${{ vars.AWS_REGION }}" \ | |
| -var "proxy_base_url=${{ vars.PROXY_BASE_URL }}" \ | |
| -var "acm_certificate_arn=${{ vars.AWS_ACM_CERTIFICATE_ARN }}" | |