From 4cda8c2b1a63db7c4648d0e88dabc6798a24aada Mon Sep 17 00:00:00 2001 From: katerynaZh Date: Sun, 27 Jul 2025 14:20:39 +0200 Subject: [PATCH 01/62] Generate DB password and write it to keyVault. Use this value when build image for BE --- .github/workflows/cicd.yaml | 20 ++++++++++++++++++-- backend_app/Dockerfile | 3 +++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index e68c72f..4f7f71c 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -55,13 +55,19 @@ jobs: if az postgres flexible-server show --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}"; then echo "PostgreSQL server already exists." else + echi "Generate password for database admin user" + PASSWORD=$(openssl rand -base64 16) + echo "Generated password: $PASSWORD" + echo "Setting AZURE_POSTGRESQL_ADMIN_PASSWORD secret in Key Vault" + az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE_POSTGRESQL_ADMIN_PASSWORD" --value "$PASSWORD" + echo "Creating PostgreSQL server..." az postgres flexible-server create \ --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ --location "${{ vars.AZURE_LOCATION }}" \ --admin-user "${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" \ - --admin-password "${{ secrets.AZURE_POSTGRESQL_ADMIN_PASSWORD }}" \ + --admin-password "$PASSWORD" \ --tier Burstable \ --sku-name Standard_B1ms \ --version 16 \ @@ -92,6 +98,14 @@ jobs: - name: Checkout code uses: actions/checkout@v2 + - name: Get DB password from Azure Key Vault + id: get_secret + uses: azure/get-keyvault-secrets@v1 + with: + keyvault: ${{ vars.AZURE_KEY_VAULT_NAME }} + secrets: | + AZURE_POSTGRESQL_ADMIN_PASSWORD + - name: Build docker images and push it to GitHub Container Registry run: | echo "Log in to GitHub Container Registry" @@ -99,7 +113,9 @@ jobs: REPO_OWNER_LOWER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') echo "Build BE docker image" - docker build -t ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest backend_app + docker build \ + --build-arg POSTGRES_PASSWORD=${{ steps.get_secret.outputs.AZURE_POSTGRESQL_ADMIN_PASSWORD }} \ + -t ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest backend_app echo "Docker image built successfully" echo "Push BE docker image to GitHub Container Registry" diff --git a/backend_app/Dockerfile b/backend_app/Dockerfile index 246dc74..02f69ea 100644 --- a/backend_app/Dockerfile +++ b/backend_app/Dockerfile @@ -4,6 +4,9 @@ FROM python:3.11-slim # Set the working directory inside the container to /app WORKDIR /app +ARG POSTGRES_PASSWORD +ENV POSTGRES_PASSWORD=$POSTGRES_PASSWORD + # Copy the requirements.txt file from the host to the container COPY requirements.txt . From 0e764776f1cf48e63521500347136fcd2300a09c Mon Sep 17 00:00:00 2001 From: katerynaZh Date: Sun, 27 Jul 2025 14:26:35 +0200 Subject: [PATCH 02/62] fix typo and format --- .github/workflows/cicd.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 4f7f71c..ab8dae9 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -55,7 +55,7 @@ jobs: if az postgres flexible-server show --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}"; then echo "PostgreSQL server already exists." else - echi "Generate password for database admin user" + echo "Generate password for database admin user" PASSWORD=$(openssl rand -base64 16) echo "Generated password: $PASSWORD" echo "Setting AZURE_POSTGRESQL_ADMIN_PASSWORD secret in Key Vault" @@ -99,12 +99,12 @@ jobs: uses: actions/checkout@v2 - name: Get DB password from Azure Key Vault - id: get_secret - uses: azure/get-keyvault-secrets@v1 - with: - keyvault: ${{ vars.AZURE_KEY_VAULT_NAME }} - secrets: | - AZURE_POSTGRESQL_ADMIN_PASSWORD + id: get_secret + uses: azure/get-keyvault-secrets@v1 + with: + keyvault: ${{ vars.AZURE_KEY_VAULT_NAME }} + secrets: | + AZURE_POSTGRESQL_ADMIN_PASSWORD - name: Build docker images and push it to GitHub Container Registry run: | @@ -114,8 +114,8 @@ jobs: echo "Build BE docker image" docker build \ - --build-arg POSTGRES_PASSWORD=${{ steps.get_secret.outputs.AZURE_POSTGRESQL_ADMIN_PASSWORD }} \ - -t ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest backend_app + --build-arg POSTGRES_PASSWORD=${{ steps.get_secret.outputs.AZURE_POSTGRESQL_ADMIN_PASSWORD }} \ + -t ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest backend_app echo "Docker image built successfully" echo "Push BE docker image to GitHub Container Registry" From b0de72115d0e3fe30675a8edcfa4d31416148f54 Mon Sep 17 00:00:00 2001 From: katerynaZh Date: Sun, 27 Jul 2025 14:30:44 +0200 Subject: [PATCH 03/62] add login to azure, before get ky vault value --- .github/workflows/cicd.yaml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index ab8dae9..73b07bf 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -25,6 +25,7 @@ jobs: client-id: ${{ vars.AZURE_CLIENT_ID }} tenant-id: ${{ vars.AZURE_TENANT_ID }} subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + - name: Create Key Vault run: | echo "Checking if Key Vault exists..." @@ -95,8 +96,12 @@ jobs: env: WEBAPP_BACKEND_NAME: ${{ vars.WEBAPP_BACKEND_NAME }} steps: - - name: Checkout code - uses: actions/checkout@v2 + - name: Login to Azure + uses: azure/login@v2 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} - name: Get DB password from Azure Key Vault id: get_secret @@ -105,6 +110,9 @@ jobs: keyvault: ${{ vars.AZURE_KEY_VAULT_NAME }} secrets: | AZURE_POSTGRESQL_ADMIN_PASSWORD + + - name: Checkout code + uses: actions/checkout@v2 - name: Build docker images and push it to GitHub Container Registry run: | From e3500a0d23d395bfccfca93dd3c1c4955cf84f04 Mon Sep 17 00:00:00 2001 From: katerynaZh Date: Sun, 27 Jul 2025 14:44:00 +0200 Subject: [PATCH 04/62] Change dependencies, as we need key vault exists when docker imageis build --- .github/workflows/cicd.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 73b07bf..0903c9d 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -93,6 +93,7 @@ jobs: name: Build docker image and save in Github Container Registry runs-on: ubuntu-latest environment: dev + needs: [create_postgres_server_and_key_vault] env: WEBAPP_BACKEND_NAME: ${{ vars.WEBAPP_BACKEND_NAME }} steps: @@ -144,7 +145,7 @@ jobs: name: Deploy Web Apps runs-on: ubuntu-latest environment: dev - needs: [create_postgres_server_and_key_vault, build_docker_image] + needs: [build_docker_image] steps: - name: Login to Azure From 32ca7c920a4094bf6a3eb56ffff2c6749844a334 Mon Sep 17 00:00:00 2001 From: katerynaZh Date: Sun, 27 Jul 2025 14:44:42 +0200 Subject: [PATCH 05/62] add trigger so actions is run automatically --- .github/workflows/cicd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 0903c9d..f0b9dce 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -5,7 +5,7 @@ on: push: branches: - main - - create_pipeline + - password_in_keyvault permissions: id-token: write From 90088350a5611653523652be9d2993834aae9630 Mon Sep 17 00:00:00 2001 From: katerynaZh Date: Sun, 27 Jul 2025 14:45:10 +0200 Subject: [PATCH 06/62] update brunch name in triggers --- .github/workflows/cicd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index f0b9dce..63702e5 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -5,7 +5,7 @@ on: push: branches: - main - - password_in_keyvault + - feature/password_in_keyvault permissions: id-token: write From 21ef128f2cdbc2e7af9d39a06868994b6318ba89 Mon Sep 17 00:00:00 2001 From: katerynaZh Date: Sun, 27 Jul 2025 14:50:06 +0200 Subject: [PATCH 07/62] fix key vault AZURE-POSTGRESQL-ADMIN-PASSWORD name --- .github/workflows/cicd.yaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 63702e5..baf9756 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -59,8 +59,6 @@ jobs: echo "Generate password for database admin user" PASSWORD=$(openssl rand -base64 16) echo "Generated password: $PASSWORD" - echo "Setting AZURE_POSTGRESQL_ADMIN_PASSWORD secret in Key Vault" - az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE_POSTGRESQL_ADMIN_PASSWORD" --value "$PASSWORD" echo "Creating PostgreSQL server..." az postgres flexible-server create \ @@ -74,7 +72,7 @@ jobs: --version 16 \ --public-access $IP fi - az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE-POSTGRESQL-ADMIN-PASSWORD" --value "${{ secrets.AZURE_POSTGRESQL_ADMIN_PASSWORD }}" + az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE-POSTGRESQL-ADMIN-PASSWORD" --value ""$PASSWORD"" az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE-POSTGRESQL-ADMIN-USER" --value "${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" echo "Create PostgreSQL database" @@ -110,7 +108,7 @@ jobs: with: keyvault: ${{ vars.AZURE_KEY_VAULT_NAME }} secrets: | - AZURE_POSTGRESQL_ADMIN_PASSWORD + AZURE-POSTGRESQL-ADMIN-PASSWORD - name: Checkout code uses: actions/checkout@v2 @@ -123,7 +121,7 @@ jobs: echo "Build BE docker image" docker build \ - --build-arg POSTGRES_PASSWORD=${{ steps.get_secret.outputs.AZURE_POSTGRESQL_ADMIN_PASSWORD }} \ + --build-arg POSTGRES_PASSWORD=${{ steps.get_secret.outputs.AZURE-POSTGRESQL-ADMIN-PASSWORD }} \ -t ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest backend_app echo "Docker image built successfully" From c2ac88a911d24cc37ec2b3ad4a444e591aa1a65e Mon Sep 17 00:00:00 2001 From: katerynaZh Date: Sun, 27 Jul 2025 15:05:55 +0200 Subject: [PATCH 08/62] try fix build docker image --- .github/workflows/cicd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index baf9756..5eefd62 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -120,7 +120,7 @@ jobs: REPO_OWNER_LOWER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') echo "Build BE docker image" - docker build \ + docker build \ --build-arg POSTGRES_PASSWORD=${{ steps.get_secret.outputs.AZURE-POSTGRESQL-ADMIN-PASSWORD }} \ -t ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest backend_app From 58017cc7be272b8a133495b9581f485f7f343b53 Mon Sep 17 00:00:00 2001 From: katerynaZh Date: Sun, 27 Jul 2025 15:16:05 +0200 Subject: [PATCH 09/62] store db password in key vault, only if db was create from scratch --- .github/workflows/cicd.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 5eefd62..6f944b1 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -71,10 +71,11 @@ jobs: --sku-name Standard_B1ms \ --version 16 \ --public-access $IP - fi - az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE-POSTGRESQL-ADMIN-PASSWORD" --value ""$PASSWORD"" - az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE-POSTGRESQL-ADMIN-USER" --value "${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" + echo "Save PostgreSQL admin user and password in Azure Key Vault" + az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE-POSTGRESQL-ADMIN-PASSWORD" --value "$PASSWORD" + az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE-POSTGRESQL-ADMIN-USER" --value "${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" + echo "Create PostgreSQL database" az postgres flexible-server db create \ --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ @@ -87,6 +88,8 @@ jobs: # --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ # --public-network-access Enabled + fi + build_docker_image: name: Build docker image and save in Github Container Registry runs-on: ubuntu-latest From 172198776310e0845cc942d5ccdac234bce50c55 Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Tue, 29 Jul 2025 21:32:21 +0200 Subject: [PATCH 10/62] add VNET --- .github/workflows/cicd.yaml | 223 ------------------------------------ 1 file changed, 223 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 6f944b1..e69de29 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -1,223 +0,0 @@ -name: "CI/CD Rest API Application" -on: - workflow_dispatch: - - push: - branches: - - main - - feature/password_in_keyvault - -permissions: - id-token: write - contents: read - packages: write - -jobs: - create_postgres_server_and_key_vault: - name: Deploy resources for restapi - runs-on: ubuntu-latest - environment: dev - - steps: - - name: Login to Azure - uses: azure/login@v2 - with: - client-id: ${{ vars.AZURE_CLIENT_ID }} - tenant-id: ${{ vars.AZURE_TENANT_ID }} - subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} - - - name: Create Key Vault - run: | - echo "Checking if Key Vault exists..." - if az keyvault show --name ${{ vars.AZURE_KEY_VAULT_NAME }} --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }}; then - echo "Key Vault already exists." - else - echo "Creating Key Vault..." - az keyvault create \ - --name ${{ vars.AZURE_KEY_VAULT_NAME }} \ - --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ - --location ${{ vars.AZURE_LOCATION }} - fi - - az role assignment create \ - --assignee ${{ vars.DEV_GROUP_ID }} \ - --role "Key Vault Secrets Officer" \ - --scope "/subscriptions/${{ vars.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ secrets.AZURE_RESOURCE_GROUP }}/providers/Microsoft.KeyVault/vaults/${{ vars.AZURE_KEY_VAULT_NAME }}" - - az role assignment create \ - --assignee ${{ vars.AZURE_CLIENT_ID }} \ - --role "Key Vault Secrets Officer" \ - --scope "/subscriptions/${{ vars.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ secrets.AZURE_RESOURCE_GROUP }}/providers/Microsoft.KeyVault/vaults/${{ vars.AZURE_KEY_VAULT_NAME }}" - - - name: Create PostgreSQL Server - run: | - IP=$(curl -s https://ifconfig.me) - echo "Runner Public IP: $IP" - if az postgres flexible-server show --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}"; then - echo "PostgreSQL server already exists." - else - echo "Generate password for database admin user" - PASSWORD=$(openssl rand -base64 16) - echo "Generated password: $PASSWORD" - - echo "Creating PostgreSQL server..." - az postgres flexible-server create \ - --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ - --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ - --location "${{ vars.AZURE_LOCATION }}" \ - --admin-user "${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" \ - --admin-password "$PASSWORD" \ - --tier Burstable \ - --sku-name Standard_B1ms \ - --version 16 \ - --public-access $IP - - echo "Save PostgreSQL admin user and password in Azure Key Vault" - az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE-POSTGRESQL-ADMIN-PASSWORD" --value "$PASSWORD" - az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE-POSTGRESQL-ADMIN-USER" --value "${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" - - echo "Create PostgreSQL database" - az postgres flexible-server db create \ - --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ - --server-name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ - --database-name tasks_db - - # echo "Allow public access from any Azure service within Azure to this server" - # az postgres flexible-server update \ - # --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ - # --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ - # --public-network-access Enabled - - fi - - build_docker_image: - name: Build docker image and save in Github Container Registry - runs-on: ubuntu-latest - environment: dev - needs: [create_postgres_server_and_key_vault] - env: - WEBAPP_BACKEND_NAME: ${{ vars.WEBAPP_BACKEND_NAME }} - steps: - - name: Login to Azure - uses: azure/login@v2 - with: - client-id: ${{ vars.AZURE_CLIENT_ID }} - tenant-id: ${{ vars.AZURE_TENANT_ID }} - subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} - - - name: Get DB password from Azure Key Vault - id: get_secret - uses: azure/get-keyvault-secrets@v1 - with: - keyvault: ${{ vars.AZURE_KEY_VAULT_NAME }} - secrets: | - AZURE-POSTGRESQL-ADMIN-PASSWORD - - - name: Checkout code - uses: actions/checkout@v2 - - - name: Build docker images and push it to GitHub Container Registry - run: | - echo "Log in to GitHub Container Registry" - echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - REPO_OWNER_LOWER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') - - echo "Build BE docker image" - docker build \ - --build-arg POSTGRES_PASSWORD=${{ steps.get_secret.outputs.AZURE-POSTGRESQL-ADMIN-PASSWORD }} \ - -t ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest backend_app - - echo "Docker image built successfully" - echo "Push BE docker image to GitHub Container Registry" - docker push ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest - - VITE_BACKEND_API_URL="https://rest-api-$WEBAPP_BACKEND_NAME.azurewebsites.net" - - echo "Build FE docker image" - docker build \ - --build-arg VITE_BACKEND_API_URL=$VITE_BACKEND_API_URL \ - -t ghcr.io/$REPO_OWNER_LOWER/rest-api-frontend:latest frontend_app - - echo "Docker image built successfully" - echo "Push FE docker image to GitHub Container Registry" - docker push ghcr.io/$REPO_OWNER_LOWER/rest-api-frontend:latest - - create_webapp: - name: Deploy Web Apps - runs-on: ubuntu-latest - environment: dev - needs: [build_docker_image] - - steps: - - name: Login to Azure - uses: azure/login@v2 - with: - client-id: ${{ vars.AZURE_CLIENT_ID }} - tenant-id: ${{ vars.AZURE_TENANT_ID }} - subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} - - name: Create App Service Plan - run: | - echo "Checking if App Service Plan exists..." - if az appservice plan show --name ${{ vars.AZURE_APP_SERVICE_PLAN_NAME }} --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }}; then - echo "App Service Plan already exists." - else - echo "Creating App Service Plan..." - az appservice plan create \ - --name ${{ vars.AZURE_APP_SERVICE_PLAN_NAME }} \ - --location ${{ vars.AZURE_LOCATION }} \ - --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ - --sku B2 \ - --is-linux - fi - - name: Create backend_app - run: | - BACKEND_WEBAPP_NAME="rest-api-${{ vars.WEBAPP_BACKEND_NAME }}" - PG_SERVER_NAME="${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}.postgres.database.azure.com" - - REPO_OWNER_LOWER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') - echo "Creating Web App..." - az webapp create \ - --name $BACKEND_WEBAPP_NAME \ - --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ - --plan ${{ vars.AZURE_APP_SERVICE_PLAN_NAME }} \ - --container-registry-url ghcr.io \ - --container-image-name $REPO_OWNER_LOWER/rest-api-backend:latest - - echo "Set environment variables for backend web app" - az webapp config appsettings set \ - --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ - --name $BACKEND_WEBAPP_NAME \ - --settings \ - POSTGRES_HOST="$PG_SERVER_NAME" \ - POSTGRES_USER="${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" \ - POSTGRES_PASSWORD="${{ secrets.AZURE_POSTGRESQL_ADMIN_PASSWORD }}" - - echo "Enable logging for backend web app" - az webapp log config \ - --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ - --name $BACKEND_WEBAPP_NAME \ - --docker-container-logging filesystem - - - name: Create frontend_app - run: | - FRONTEND_WEBAPP_NAME="rest-api-${{ vars.WEBAPP_FRONTEND_NAME }}" - REPO_OWNER_LOWER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') - echo "Creating Web App..." - az webapp create \ - --name "$FRONTEND_WEBAPP_NAME" \ - --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ - --plan ${{ vars.AZURE_APP_SERVICE_PLAN_NAME }} \ - --container-registry-url ghcr.io \ - --container-image-name $REPO_OWNER_LOWER/rest-api-frontend:latest - - echo "Configuring frontend_app settings..." - az webapp config appsettings set \ - --name $FRONTEND_WEBAPP_NAME \ - --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ - --settings "VITE_BACKEND_API_URL=https://rest-api-${{ vars.WEBAPP_BACKEND_NAME }}.azurewebsites.net" - - echo "Enable logging for frontend web app" - az webapp log config \ - --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ - --name $FRONTEND_WEBAPP_NAME \ - --docker-container-logging filesystem From bd1e7e31a20e7784b1babb66d9008ce1039fe095 Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Tue, 29 Jul 2025 21:33:02 +0200 Subject: [PATCH 11/62] add VNET --- .github/workflows/cicd.yaml | 271 ++++++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index e69de29..2ca45af 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -0,0 +1,271 @@ +name: "CI/CD Rest API Application" +on: + workflow_dispatch: + + push: + branches: + - main + - feature/password_in_keyvault + +permissions: + id-token: write + contents: read + packages: write + +jobs: + create_postgres_server_and_key_vault: + name: Deploy resources for restapi + runs-on: ubuntu-latest + environment: dev + + steps: + - name: Login to Azure + uses: azure/login@v2 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + + - name: Create VNet + run: | + echo "Checking if VNet exists..." + if az network vnet show --name ${{ vars.AZURE_VNET_NAME }} --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }}; then + echo "VNet already exists." + else + echo "Creating VNet..." + az network vnet create \ + --name ${{ vars.AZURE_VNET_NAME }} \ + --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ + --location ${{ vars.AZURE_LOCATION }} \ + --address-prefixes 10.0.0.0/16 + fi + address-prefix=1 + for subnet in ${{ vars.AZURE_SUBNET_BE }} ${{ vars.AZURE_SUBNET_FE }} ${{ vars.AZURE_SUBNET_DB }} ; do + echo "Creating Subnet for $subnet..." + az network vnet subnet create \ + --name $subnet \ + --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ + --vnet-name ${{ vars.AZURE_VNET_NAME }} \ + --address-prefixes 10.0.$address-prefix.0/24 + address-prefix=$((address-prefix + 1)) + done + + - name: Create Key Vault + run: | + echo "Checking if Key Vault exists..." + if az keyvault show --name ${{ vars.AZURE_KEY_VAULT_NAME }} --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }}; then + echo "Key Vault already exists." + else + echo "Creating Key Vault..." + az keyvault create \ + --name ${{ vars.AZURE_KEY_VAULT_NAME }} \ + --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ + --location ${{ vars.AZURE_LOCATION }} + fi + + az role assignment create \ + --assignee ${{ vars.DEV_GROUP_ID }} \ + --role "Key Vault Secrets Officer" \ + --scope "/subscriptions/${{ vars.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ secrets.AZURE_RESOURCE_GROUP }}/providers/Microsoft.KeyVault/vaults/${{ vars.AZURE_KEY_VAULT_NAME }}" + + az role assignment create \ + --assignee ${{ vars.AZURE_CLIENT_ID }} \ + --role "Key Vault Secrets Officer" \ + --scope "/subscriptions/${{ vars.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ secrets.AZURE_RESOURCE_GROUP }}/providers/Microsoft.KeyVault/vaults/${{ vars.AZURE_KEY_VAULT_NAME }}" + + - name: Create PostgreSQL Server + run: | + IP=$(curl -s https://ifconfig.me) + echo "Runner Public IP: $IP" + if az postgres flexible-server show --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}"; then + echo "PostgreSQL server already exists." + else + echo "Generate password for database admin user" + PASSWORD=$(openssl rand -base64 16) + echo "Generated password: $PASSWORD" + + echo "Creating PostgreSQL server..." + az postgres flexible-server create \ + --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ + --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ + --location "${{ vars.AZURE_LOCATION }}" \ + --admin-user "${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" \ + --admin-password "$PASSWORD" \ + --tier Burstable \ + --sku-name Standard_B1ms \ + --version 16 \ + --public-access $IP + + echo "VNet integration for PostgreSQL server" + az postgres flexible-server vnet-integration create \ + --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ + --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ + --vnet "${{ vars.AZURE_VNET_NAME }}" \ + --subnet "${{ vars.AZURE_SUBNET_DB }}" \ + --no-wait + + echo "Save PostgreSQL admin user and password in Azure Key Vault" + az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE-POSTGRESQL-ADMIN-PASSWORD" --value "$PASSWORD" + az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE-POSTGRESQL-ADMIN-USER" --value "${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" + + echo "Create PostgreSQL database" + az postgres flexible-server db create \ + --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ + --server-name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ + --database-name tasks_db + + # echo "Allow public access from any Azure service within Azure to this server" + # az postgres flexible-server update \ + # --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ + # --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ + # --public-network-access Enabled + + fi + + build_docker_image: + name: Build docker image and save in Github Container Registry + runs-on: ubuntu-latest + environment: dev + needs: [create_postgres_server_and_key_vault] + env: + WEBAPP_BACKEND_NAME: ${{ vars.WEBAPP_BACKEND_NAME }} + steps: + - name: Login to Azure + uses: azure/login@v2 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + + - name: Get DB password from Azure Key Vault + id: get_secret + uses: azure/get-keyvault-secrets@v1 + with: + keyvault: ${{ vars.AZURE_KEY_VAULT_NAME }} + secrets: | + AZURE-POSTGRESQL-ADMIN-PASSWORD + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Build docker images and push it to GitHub Container Registry + run: | + echo "Log in to GitHub Container Registry" + echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + REPO_OWNER_LOWER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') + + echo "Build BE docker image" + docker build \ + --build-arg POSTGRES_PASSWORD=${{ steps.get_secret.outputs.AZURE-POSTGRESQL-ADMIN-PASSWORD }} \ + -t ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest backend_app + + echo "Docker image built successfully" + echo "Push BE docker image to GitHub Container Registry" + docker push ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest + + VITE_BACKEND_API_URL="https://rest-api-$WEBAPP_BACKEND_NAME.azurewebsites.net" + + echo "Build FE docker image" + docker build \ + --build-arg VITE_BACKEND_API_URL=$VITE_BACKEND_API_URL \ + -t ghcr.io/$REPO_OWNER_LOWER/rest-api-frontend:latest frontend_app + + echo "Docker image built successfully" + echo "Push FE docker image to GitHub Container Registry" + docker push ghcr.io/$REPO_OWNER_LOWER/rest-api-frontend:latest + + create_webapp: + name: Deploy Web Apps + runs-on: ubuntu-latest + environment: dev + needs: [build_docker_image] + + steps: + - name: Login to Azure + uses: azure/login@v2 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + - name: Create App Service Plan + run: | + echo "Checking if App Service Plan exists..." + if az appservice plan show --name ${{ vars.AZURE_APP_SERVICE_PLAN_NAME }} --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }}; then + echo "App Service Plan already exists." + else + echo "Creating App Service Plan..." + az appservice plan create \ + --name ${{ vars.AZURE_APP_SERVICE_PLAN_NAME }} \ + --location ${{ vars.AZURE_LOCATION }} \ + --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ + --sku B2 \ + --is-linux + fi + - name: Create backend_app + run: | + BACKEND_WEBAPP_NAME="rest-api-${{ vars.WEBAPP_BACKEND_NAME }}" + PG_SERVER_NAME="${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}.postgres.database.azure.com" + + REPO_OWNER_LOWER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') + echo "Creating Web App..." + az webapp create \ + --name $BACKEND_WEBAPP_NAME \ + --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ + --plan ${{ vars.AZURE_APP_SERVICE_PLAN_NAME }} \ + --container-registry-url ghcr.io \ + --container-image-name $REPO_OWNER_LOWER/rest-api-backend:latest + + echo "Set environment variables for backend web app" + az webapp config appsettings set \ + --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ + --name $BACKEND_WEBAPP_NAME \ + --settings \ + POSTGRES_HOST="$PG_SERVER_NAME" \ + POSTGRES_USER="${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" \ + POSTGRES_PASSWORD="${{ secrets.AZURE_POSTGRESQL_ADMIN_PASSWORD }}" + + echo "Enable logging for backend web app" + az webapp log config \ + --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ + --name $BACKEND_WEBAPP_NAME \ + --docker-container-logging filesystem + + echo "VNet integration for backend web app" + az postgres flexible-server vnet-integration create \ + --name "$BACKEND_WEBAPP_NAME" \ + --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ + --vnet "${{ vars.AZURE_VNET_NAME }}" \ + --subnet "${{ vars.AZURE_SUBNET_BE }}" \ + --no-wait + + - name: Create frontend_app + run: | + FRONTEND_WEBAPP_NAME="rest-api-${{ vars.WEBAPP_FRONTEND_NAME }}" + REPO_OWNER_LOWER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') + echo "Creating Web App..." + az webapp create \ + --name "$FRONTEND_WEBAPP_NAME" \ + --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ + --plan ${{ vars.AZURE_APP_SERVICE_PLAN_NAME }} \ + --container-registry-url ghcr.io \ + --container-image-name $REPO_OWNER_LOWER/rest-api-frontend:latest + + echo "Configuring frontend_app settings..." + az webapp config appsettings set \ + --name $FRONTEND_WEBAPP_NAME \ + --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ + --settings "VITE_BACKEND_API_URL=https://rest-api-${{ vars.WEBAPP_BACKEND_NAME }}.azurewebsites.net" + + echo "Enable logging for frontend web app" + az webapp log config \ + --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ + --name $FRONTEND_WEBAPP_NAME \ + --docker-container-logging filesystem + + echo "VNet integration for frontend web app" + az postgres flexible-server vnet-integration create \ + --name "$FRONTEND_WEBAPP_NAME" \ + --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ + --vnet "${{ vars.AZURE_VNET_NAME }}" \ + --subnet "${{ vars.AZURE_SUBNET_FE }}" \ + --no-wait From d0f8165f5a873b24b509af38593983e7e4a06b0a Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Tue, 29 Jul 2025 21:41:07 +0200 Subject: [PATCH 12/62] change address_prefix --- .github/workflows/cicd.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 2ca45af..c742301 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -39,15 +39,15 @@ jobs: --location ${{ vars.AZURE_LOCATION }} \ --address-prefixes 10.0.0.0/16 fi - address-prefix=1 + address_prefix=1 for subnet in ${{ vars.AZURE_SUBNET_BE }} ${{ vars.AZURE_SUBNET_FE }} ${{ vars.AZURE_SUBNET_DB }} ; do echo "Creating Subnet for $subnet..." az network vnet subnet create \ --name $subnet \ --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ --vnet-name ${{ vars.AZURE_VNET_NAME }} \ - --address-prefixes 10.0.$address-prefix.0/24 - address-prefix=$((address-prefix + 1)) + --address-prefixes 10.0.$address_prefix.0/24 + address_prefix=$((address_prefix + 1)) done - name: Create Key Vault From 97ae31cf75f8946241b8543c65ce0f025523bebd Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Tue, 29 Jul 2025 21:50:33 +0200 Subject: [PATCH 13/62] fixed vnet-integration --- .github/workflows/cicd.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index c742301..ba7c5b5 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -231,7 +231,7 @@ jobs: --docker-container-logging filesystem echo "VNet integration for backend web app" - az postgres flexible-server vnet-integration create \ + az webapp vnet-integration create \ --name "$BACKEND_WEBAPP_NAME" \ --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ --vnet "${{ vars.AZURE_VNET_NAME }}" \ @@ -263,7 +263,7 @@ jobs: --docker-container-logging filesystem echo "VNet integration for frontend web app" - az postgres flexible-server vnet-integration create \ + az webapp vnet-integration create \ --name "$FRONTEND_WEBAPP_NAME" \ --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ --vnet "${{ vars.AZURE_VNET_NAME }}" \ From 9e6e7420664e535db85b238d3b28d9d28fc4da5c Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Tue, 29 Jul 2025 22:02:53 +0200 Subject: [PATCH 14/62] change create to add --- .github/workflows/cicd.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index ba7c5b5..f9c8aaa 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -231,7 +231,7 @@ jobs: --docker-container-logging filesystem echo "VNet integration for backend web app" - az webapp vnet-integration create \ + az webapp vnet-integration add \ --name "$BACKEND_WEBAPP_NAME" \ --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ --vnet "${{ vars.AZURE_VNET_NAME }}" \ @@ -263,7 +263,7 @@ jobs: --docker-container-logging filesystem echo "VNet integration for frontend web app" - az webapp vnet-integration create \ + az webapp vnet-integration add \ --name "$FRONTEND_WEBAPP_NAME" \ --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ --vnet "${{ vars.AZURE_VNET_NAME }}" \ From 6c9351cb06aa6eaed87d69d11ca51af0775aa60e Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Tue, 29 Jul 2025 22:17:23 +0200 Subject: [PATCH 15/62] fixed --- .github/workflows/cicd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index f9c8aaa..80966cd 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -232,7 +232,7 @@ jobs: echo "VNet integration for backend web app" az webapp vnet-integration add \ - --name "$BACKEND_WEBAPP_NAME" \ + --name "$BACKEND_WEBAPP_NAME" \ --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ --vnet "${{ vars.AZURE_VNET_NAME }}" \ --subnet "${{ vars.AZURE_SUBNET_BE }}" \ From 3f03c6dcd8fe68365e9ac459fc5b94a4f501c237 Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Tue, 29 Jul 2025 22:23:20 +0200 Subject: [PATCH 16/62] delete no wait --- .github/workflows/cicd.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 80966cd..a9c4efc 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -235,8 +235,7 @@ jobs: --name "$BACKEND_WEBAPP_NAME" \ --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ --vnet "${{ vars.AZURE_VNET_NAME }}" \ - --subnet "${{ vars.AZURE_SUBNET_BE }}" \ - --no-wait + --subnet "${{ vars.AZURE_SUBNET_BE }}" - name: Create frontend_app run: | @@ -267,5 +266,4 @@ jobs: --name "$FRONTEND_WEBAPP_NAME" \ --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ --vnet "${{ vars.AZURE_VNET_NAME }}" \ - --subnet "${{ vars.AZURE_SUBNET_FE }}" \ - --no-wait + --subnet "${{ vars.AZURE_SUBNET_FE }}" From 6d4e1ee5da3f59cfa75b36e6555c2bbff79327cc Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Tue, 29 Jul 2025 22:29:13 +0200 Subject: [PATCH 17/62] fixed --- .github/workflows/cicd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index a9c4efc..4313f36 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -263,7 +263,7 @@ jobs: echo "VNet integration for frontend web app" az webapp vnet-integration add \ - --name "$FRONTEND_WEBAPP_NAME" \ + --name "$FRONTEND_WEBAPP_NAME" \ --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ --vnet "${{ vars.AZURE_VNET_NAME }}" \ --subnet "${{ vars.AZURE_SUBNET_FE }}" From b478b3c3b332d3967482fefa244d3c14748160e6 Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Tue, 29 Jul 2025 22:35:39 +0200 Subject: [PATCH 18/62] fixd create vnet --- .github/workflows/cicd.yaml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 4313f36..7e3f44f 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -42,11 +42,20 @@ jobs: address_prefix=1 for subnet in ${{ vars.AZURE_SUBNET_BE }} ${{ vars.AZURE_SUBNET_FE }} ${{ vars.AZURE_SUBNET_DB }} ; do echo "Creating Subnet for $subnet..." - az network vnet subnet create \ - --name $subnet \ - --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ - --vnet-name ${{ vars.AZURE_VNET_NAME }} \ - --address-prefixes 10.0.$address_prefix.0/24 + if [[ "$subnet" == "${{ vars.AZURE_SUBNET_BE }}" ]] || [[ "$subnet" == "${{ vars.AZURE_SUBNET_FE }}" ]]; then + az network vnet subnet create \ + --name $subnet \ + --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ + --vnet-name ${{ vars.AZURE_VNET_NAME }} \ + --address-prefixes 10.0.$address_prefix.0/24 \ + --delegations Microsoft.Web/serverFarms + else + az network vnet subnet create \ + --name $subnet \ + --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ + --vnet-name ${{ vars.AZURE_VNET_NAME }} \ + --address-prefixes 10.0.$address_prefix.0/24 + fi address_prefix=$((address_prefix + 1)) done From 80163e79f662e88f47e272ca55063e90370f2ac0 Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Mon, 4 Aug 2025 20:58:33 +0200 Subject: [PATCH 19/62] fixed --- .github/workflows/cicd.yaml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 7e3f44f..020398e 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -37,7 +37,7 @@ jobs: --name ${{ vars.AZURE_VNET_NAME }} \ --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ --location ${{ vars.AZURE_LOCATION }} \ - --address-prefixes 10.0.0.0/16 + --address-prefixes 10.0.0.0/16 fi address_prefix=1 for subnet in ${{ vars.AZURE_SUBNET_BE }} ${{ vars.AZURE_SUBNET_FE }} ${{ vars.AZURE_SUBNET_DB }} ; do @@ -71,7 +71,7 @@ jobs: --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ --location ${{ vars.AZURE_LOCATION }} fi - + az role assignment create \ --assignee ${{ vars.DEV_GROUP_ID }} \ --role "Key Vault Secrets Officer" \ @@ -107,12 +107,12 @@ jobs: echo "VNet integration for PostgreSQL server" az postgres flexible-server vnet-integration create \ - --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ + --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ --vnet "${{ vars.AZURE_VNET_NAME }}" \ --subnet "${{ vars.AZURE_SUBNET_DB }}" \ - --no-wait - + --no-wait + echo "Save PostgreSQL admin user and password in Azure Key Vault" az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE-POSTGRESQL-ADMIN-PASSWORD" --value "$PASSWORD" az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE-POSTGRESQL-ADMIN-USER" --value "${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" @@ -196,6 +196,7 @@ jobs: client-id: ${{ vars.AZURE_CLIENT_ID }} tenant-id: ${{ vars.AZURE_TENANT_ID }} subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + - name: Create App Service Plan run: | echo "Checking if App Service Plan exists..." @@ -237,7 +238,7 @@ jobs: az webapp log config \ --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ --name $BACKEND_WEBAPP_NAME \ - --docker-container-logging filesystem + --docker-container-logging filesystem echo "VNet integration for backend web app" az webapp vnet-integration add \ From 757ecc14fb3ea310978563c159ace703508f4004 Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Mon, 4 Aug 2025 21:08:36 +0200 Subject: [PATCH 20/62] add "" --- .github/workflows/cicd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 020398e..22c0a11 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -165,7 +165,7 @@ jobs: echo "Build BE docker image" docker build \ - --build-arg POSTGRES_PASSWORD=${{ steps.get_secret.outputs.AZURE-POSTGRESQL-ADMIN-PASSWORD }} \ + --build-arg POSTGRES_PASSWORD='${{ steps.get_secret.outputs.AZURE-POSTGRESQL-ADMIN-PASSWORD }}' \ -t ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest backend_app echo "Docker image built successfully" From b1341cde7faeec9def1b298872bfd88a2f364358 Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Mon, 4 Aug 2025 21:44:39 +0200 Subject: [PATCH 21/62] change "" --- .github/workflows/cicd.yaml | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 22c0a11..5001c96 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -103,15 +103,16 @@ jobs: --tier Burstable \ --sku-name Standard_B1ms \ --version 16 \ - --public-access $IP - - echo "VNet integration for PostgreSQL server" - az postgres flexible-server vnet-integration create \ - --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ - --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ --vnet "${{ vars.AZURE_VNET_NAME }}" \ - --subnet "${{ vars.AZURE_SUBNET_DB }}" \ - --no-wait + --subnet "${{ vars.AZURE_SUBNET_DB }}" + + # echo "VNet integration for PostgreSQL server" + # az postgres flexible-server vnet-integration create \ + # --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ + # --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ + # --vnet "${{ vars.AZURE_VNET_NAME }}" \ + # --subnet "${{ vars.AZURE_SUBNET_DB }}" \ + # --no-wait echo "Save PostgreSQL admin user and password in Azure Key Vault" az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE-POSTGRESQL-ADMIN-PASSWORD" --value "$PASSWORD" @@ -165,7 +166,7 @@ jobs: echo "Build BE docker image" docker build \ - --build-arg POSTGRES_PASSWORD='${{ steps.get_secret.outputs.AZURE-POSTGRESQL-ADMIN-PASSWORD }}' \ + --build-arg POSTGRES_PASSWORD="${{ steps.get_secret.outputs.AZURE-POSTGRESQL-ADMIN-PASSWORD }}" \ -t ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest backend_app echo "Docker image built successfully" From 3e6745b37cb0fa7cb16e51a4f44700a2d4ad9a9c Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Mon, 4 Aug 2025 21:52:10 +0200 Subject: [PATCH 22/62] add yes --- .github/workflows/cicd.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 5001c96..bda6773 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -104,7 +104,8 @@ jobs: --sku-name Standard_B1ms \ --version 16 \ --vnet "${{ vars.AZURE_VNET_NAME }}" \ - --subnet "${{ vars.AZURE_SUBNET_DB }}" + --subnet "${{ vars.AZURE_SUBNET_DB }}" \ + --yes # echo "VNet integration for PostgreSQL server" # az postgres flexible-server vnet-integration create \ From 1daf7b7fca02cea4c26f5305b4adc79a4836e947 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Wed, 6 Aug 2025 21:01:55 +0200 Subject: [PATCH 23/62] update CI/CD workflow for improved resource deployment --- .github/workflows/cicd.yaml | 44 ++++++++++++++----------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index bda6773..82db997 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -14,6 +14,8 @@ permissions: jobs: create_postgres_server_and_key_vault: + outputs: + AZURE-POSTGRESQL-ADMIN-PASSWORD: ${{ steps.create_postgres_server.outputs.AZURE-POSTGRESQL-ADMIN-PASSWORD }} name: Deploy resources for restapi runs-on: ubuntu-latest environment: dev @@ -83,15 +85,15 @@ jobs: --scope "/subscriptions/${{ vars.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ secrets.AZURE_RESOURCE_GROUP }}/providers/Microsoft.KeyVault/vaults/${{ vars.AZURE_KEY_VAULT_NAME }}" - name: Create PostgreSQL Server + id: create_postgres_server run: | - IP=$(curl -s https://ifconfig.me) - echo "Runner Public IP: $IP" if az postgres flexible-server show --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}"; then echo "PostgreSQL server already exists." + echo "Fetching PostgreSQL admin password from Azure Key Vault" + PASSWORD=$(az keyvault secret show --name "AZURE-POSTGRESQL-ADMIN-PASSWORD" --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --query value -o tsv) else echo "Generate password for database admin user" PASSWORD=$(openssl rand -base64 16) - echo "Generated password: $PASSWORD" echo "Creating PostgreSQL server..." az postgres flexible-server create \ @@ -107,14 +109,6 @@ jobs: --subnet "${{ vars.AZURE_SUBNET_DB }}" \ --yes - # echo "VNet integration for PostgreSQL server" - # az postgres flexible-server vnet-integration create \ - # --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ - # --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ - # --vnet "${{ vars.AZURE_VNET_NAME }}" \ - # --subnet "${{ vars.AZURE_SUBNET_DB }}" \ - # --no-wait - echo "Save PostgreSQL admin user and password in Azure Key Vault" az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE-POSTGRESQL-ADMIN-PASSWORD" --value "$PASSWORD" az keyvault secret set --vault-name "${{ vars.AZURE_KEY_VAULT_NAME }}" --name "AZURE-POSTGRESQL-ADMIN-USER" --value "${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" @@ -124,22 +118,21 @@ jobs: --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ --server-name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ --database-name tasks_db + fi - # echo "Allow public access from any Azure service within Azure to this server" - # az postgres flexible-server update \ - # --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ - # --name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ - # --public-network-access Enabled - - fi + echo "::add-mask::$PASSWORD" + echo "AZURE-POSTGRESQL-ADMIN-PASSWORD=$PASSWORD" >> $GITHUB_OUTPUT build_docker_image: name: Build docker image and save in Github Container Registry + outputs: + AZURE-POSTGRESQL-ADMIN-PASSWORD: ${{ env.PG_PASSWORD }} runs-on: ubuntu-latest environment: dev needs: [create_postgres_server_and_key_vault] env: WEBAPP_BACKEND_NAME: ${{ vars.WEBAPP_BACKEND_NAME }} + PG_PASSWORD: ${{ needs.create_postgres_server_and_key_vault.outputs.AZURE-POSTGRESQL-ADMIN-PASSWORD }} steps: - name: Login to Azure uses: azure/login@v2 @@ -148,14 +141,6 @@ jobs: tenant-id: ${{ vars.AZURE_TENANT_ID }} subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} - - name: Get DB password from Azure Key Vault - id: get_secret - uses: azure/get-keyvault-secrets@v1 - with: - keyvault: ${{ vars.AZURE_KEY_VAULT_NAME }} - secrets: | - AZURE-POSTGRESQL-ADMIN-PASSWORD - - name: Checkout code uses: actions/checkout@v2 @@ -167,7 +152,7 @@ jobs: echo "Build BE docker image" docker build \ - --build-arg POSTGRES_PASSWORD="${{ steps.get_secret.outputs.AZURE-POSTGRESQL-ADMIN-PASSWORD }}" \ + --build-arg POSTGRES_PASSWORD="${{ env.PG_PASSWORD }}" \ -t ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest backend_app echo "Docker image built successfully" @@ -213,7 +198,10 @@ jobs: --sku B2 \ --is-linux fi + - name: Create backend_app + env: + PG_PASSWORD: ${{ needs.build_docker_image.outputs.AZURE-POSTGRESQL-ADMIN-PASSWORD }} run: | BACKEND_WEBAPP_NAME="rest-api-${{ vars.WEBAPP_BACKEND_NAME }}" PG_SERVER_NAME="${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}.postgres.database.azure.com" @@ -234,7 +222,7 @@ jobs: --settings \ POSTGRES_HOST="$PG_SERVER_NAME" \ POSTGRES_USER="${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" \ - POSTGRES_PASSWORD="${{ secrets.AZURE_POSTGRESQL_ADMIN_PASSWORD }}" + POSTGRES_PASSWORD="${{ env.PG_PASSWORD }}" echo "Enable logging for backend web app" az webapp log config \ From 2c653a4239ec7c8eda74fb3f27d41b9c1c64c51f Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Wed, 6 Aug 2025 22:14:42 +0200 Subject: [PATCH 24/62] add CreateNSGRule action and integrate into CI/CD workflow for NSG management --- .github/actions/CreateNSGRule/action.yaml | 64 +++++++++++++++++++++++ .github/workflows/cicd.yaml | 49 ++++++++++++----- 2 files changed, 101 insertions(+), 12 deletions(-) create mode 100644 .github/actions/CreateNSGRule/action.yaml diff --git a/.github/actions/CreateNSGRule/action.yaml b/.github/actions/CreateNSGRule/action.yaml new file mode 100644 index 0000000..3cd48eb --- /dev/null +++ b/.github/actions/CreateNSGRule/action.yaml @@ -0,0 +1,64 @@ +name: Create NSG Rule + +inputs: + AZURE_RESOURCE_GROUP: + description: 'Azure Resource Group Name' + required: true + type: string + AZURE_NSG_NAME: + description: 'Azure NSG Name' + required: true + type: string + AZURE_LOCATION: + description: 'Azure Location' + required: true + type: string + NSG_RULE_NAME: + description: 'Azure NSG Rule Name' + required: false + type: string + default: 'AllowTraffic' + ALLOWED_PORTS: + description: 'Comma-separated list of allowed ports' + required: false + type: string + default: '80,443' + +runs: + using: composite + steps: + - name: Create ${{ inputs.AZURE_NSG_NAME }} NSG Rule Port + run: | + | + echo "Checking if '${{ inputs.AZURE_NSG_NAME }}' NSG for VNet exists..." + if az network nsg show --name ${{ inputs.AZURE_NSG_NAME }} --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }}; then + echo "NSG already exists." + else + echo "Creating NSG for VNet..." + az network nsg create \ + --name ${{ inputs.AZURE_NSG_NAME }} \ + --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} \ + --location ${{ inputs.AZURE_LOCATION }} + fi + + PORTS=$(echo ${{ inputs.ALLOWED_PORTS }} | tr ',' ' ') + for PORT in $PORTS; do + echo "Checking if NSG rule for port $PORT exists..." + if az network nsg rule show --nsg-name ${{ inputs.AZURE_NSG_NAME }} --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} --name "${{ inputs.NSG_RULE_NAME}}-$PORT"; then + echo "NSG rule for port $PORT already exists." + else + echo "Creating NSG rule for port $PORT..." + az network nsg rule create \ + --nsg-name ${{ inputs.AZURE_NSG_NAME }} \ + --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} \ + --name ${{ inputs.NSG_RULE_NAME}}-$PORT \ + --priority 1000 \ + --direction Inbound \ + --access Allow \ + --protocol Tcp \ + --source-address-prefixes '*' \ + --source-port-ranges '*' \ + --destination-address-prefixes '*' \ + --destination-port-ranges $PORT + fi + done diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 82db997..c25b492 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -13,14 +13,16 @@ permissions: packages: write jobs: - create_postgres_server_and_key_vault: - outputs: - AZURE-POSTGRESQL-ADMIN-PASSWORD: ${{ steps.create_postgres_server.outputs.AZURE-POSTGRESQL-ADMIN-PASSWORD }} + create_core_services: + # Network, KeyVault, PostgreSQL name: Deploy resources for restapi runs-on: ubuntu-latest environment: dev steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Login to Azure uses: azure/login@v2 with: @@ -61,6 +63,34 @@ jobs: address_prefix=$((address_prefix + 1)) done + - name: Create FE NSG + uses: ./.github/actions/CreateNSGRule + with: + AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} + AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_FE }} + AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} + NSG_RULE_NAME: ${{ vars.NSG_RULE_NAME }} + ALLOWED_PORTS: '80,443' + + - name: Create BE NSG + uses: ./.github/actions/CreateNSGRule + with: + AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} + AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_BE }} + AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} + NSG_RULE_NAME: ${{ vars.NSG_RULE_NAME }} + ALLOWED_PORTS: '80,443' + + - name: Create DB NSG + uses: ./.github/actions/CreateNSGRule + with: + AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} + AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_DB }} + AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} + NSG_RULE_NAME: ${{ vars.NSG_RULE_NAME }} + ALLOWED_PORTS: '5432' + + - name: Create Key Vault run: | echo "Checking if Key Vault exists..." @@ -121,18 +151,15 @@ jobs: fi echo "::add-mask::$PASSWORD" - echo "AZURE-POSTGRESQL-ADMIN-PASSWORD=$PASSWORD" >> $GITHUB_OUTPUT + echo "AZURE-POSTGRESQL-ADMIN-PASSWORD=$PASSWORD" >> $GITHUB_ENV build_docker_image: name: Build docker image and save in Github Container Registry - outputs: - AZURE-POSTGRESQL-ADMIN-PASSWORD: ${{ env.PG_PASSWORD }} runs-on: ubuntu-latest environment: dev - needs: [create_postgres_server_and_key_vault] + needs: [create_core_services] env: WEBAPP_BACKEND_NAME: ${{ vars.WEBAPP_BACKEND_NAME }} - PG_PASSWORD: ${{ needs.create_postgres_server_and_key_vault.outputs.AZURE-POSTGRESQL-ADMIN-PASSWORD }} steps: - name: Login to Azure uses: azure/login@v2 @@ -152,7 +179,7 @@ jobs: echo "Build BE docker image" docker build \ - --build-arg POSTGRES_PASSWORD="${{ env.PG_PASSWORD }}" \ + --build-arg POSTGRES_PASSWORD="${{ env.AZURE-POSTGRESQL-ADMIN-PASSWORD }}" \ -t ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest backend_app echo "Docker image built successfully" @@ -200,8 +227,6 @@ jobs: fi - name: Create backend_app - env: - PG_PASSWORD: ${{ needs.build_docker_image.outputs.AZURE-POSTGRESQL-ADMIN-PASSWORD }} run: | BACKEND_WEBAPP_NAME="rest-api-${{ vars.WEBAPP_BACKEND_NAME }}" PG_SERVER_NAME="${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}.postgres.database.azure.com" @@ -222,7 +247,7 @@ jobs: --settings \ POSTGRES_HOST="$PG_SERVER_NAME" \ POSTGRES_USER="${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" \ - POSTGRES_PASSWORD="${{ env.PG_PASSWORD }}" + POSTGRES_PASSWORD="${{ env.AZURE-POSTGRESQL-ADMIN-PASSWORD }}" echo "Enable logging for backend web app" az webapp log config \ From 10682d526ebdf2af29bb5d10502efa25fad25b1c Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Thu, 7 Aug 2025 20:53:53 +0200 Subject: [PATCH 25/62] added shell in the action --- .github/actions/CreateNSGRule/action.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/CreateNSGRule/action.yaml b/.github/actions/CreateNSGRule/action.yaml index 3cd48eb..63fcae2 100644 --- a/.github/actions/CreateNSGRule/action.yaml +++ b/.github/actions/CreateNSGRule/action.yaml @@ -27,9 +27,9 @@ inputs: runs: using: composite steps: - - name: Create ${{ inputs.AZURE_NSG_NAME }} NSG Rule Port + - name: Create ${{ inputs.AZURE_NSG_NAME }} NSG Rule Port + shell: bash run: | - | echo "Checking if '${{ inputs.AZURE_NSG_NAME }}' NSG for VNet exists..." if az network nsg show --name ${{ inputs.AZURE_NSG_NAME }} --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }}; then echo "NSG already exists." From 85c29a9e02517ee6986533d8e842b59092889354 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Thu, 7 Aug 2025 20:56:47 +0200 Subject: [PATCH 26/62] rule name updated --- .github/workflows/cicd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index c25b492..fc3eb5c 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -69,7 +69,7 @@ jobs: AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_FE }} AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} - NSG_RULE_NAME: ${{ vars.NSG_RULE_NAME }} + NSG_RULE_NAME: "AllowTraffic4Port" ALLOWED_PORTS: '80,443' - name: Create BE NSG From 65e67cace18c87105a055b05a835d4b78feb9bec Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Thu, 7 Aug 2025 21:18:13 +0200 Subject: [PATCH 27/62] prio fixup --- .github/actions/CreateNSGRule/action.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/actions/CreateNSGRule/action.yaml b/.github/actions/CreateNSGRule/action.yaml index 63fcae2..e6db112 100644 --- a/.github/actions/CreateNSGRule/action.yaml +++ b/.github/actions/CreateNSGRule/action.yaml @@ -42,17 +42,18 @@ runs: fi PORTS=$(echo ${{ inputs.ALLOWED_PORTS }} | tr ',' ' ') + PRIO=1000 for PORT in $PORTS; do echo "Checking if NSG rule for port $PORT exists..." - if az network nsg rule show --nsg-name ${{ inputs.AZURE_NSG_NAME }} --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} --name "${{ inputs.NSG_RULE_NAME}}-$PORT"; then + if az network nsg rule show --nsg-name ${{ inputs.AZURE_NSG_NAME }} --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} --name "${{ inputs.NSG_RULE_NAME}}-$PORT-pr-$PRIO"; then echo "NSG rule for port $PORT already exists." else echo "Creating NSG rule for port $PORT..." az network nsg rule create \ --nsg-name ${{ inputs.AZURE_NSG_NAME }} \ --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} \ - --name ${{ inputs.NSG_RULE_NAME}}-$PORT \ - --priority 1000 \ + --name ${{ inputs.NSG_RULE_NAME}}-$PORT-pr-$PRIO \ + --priority $PRIO \ --direction Inbound \ --access Allow \ --protocol Tcp \ @@ -61,4 +62,5 @@ runs: --destination-address-prefixes '*' \ --destination-port-ranges $PORT fi + PRIO=$((PRIO + 100)) done From 7c28ba00633e0b21d2ce5021e892e9731d35d777 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Thu, 7 Aug 2025 21:24:10 +0200 Subject: [PATCH 28/62] updated nsg rule names --- .github/workflows/cicd.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index fc3eb5c..430fe74 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -78,7 +78,7 @@ jobs: AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_BE }} AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} - NSG_RULE_NAME: ${{ vars.NSG_RULE_NAME }} + NSG_RULE_NAME: "AllowTraffic4Port" ALLOWED_PORTS: '80,443' - name: Create DB NSG @@ -87,7 +87,7 @@ jobs: AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_DB }} AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} - NSG_RULE_NAME: ${{ vars.NSG_RULE_NAME }} + NSG_RULE_NAME: "AllowTraffic4Port" ALLOWED_PORTS: '5432' From 921f6b7c6b728686604ec9b7674a28adf9875a5e Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Thu, 7 Aug 2025 22:40:33 +0200 Subject: [PATCH 29/62] add CreateDockerImage action and integrate into CI/CD workflow for building and pushing Docker images --- .github/actions/CreateDockerImage/action.yaml | 71 +++++++++++++++ .github/workflows/cicd.yaml | 86 ++++++++++++------- 2 files changed, 125 insertions(+), 32 deletions(-) create mode 100644 .github/actions/CreateDockerImage/action.yaml diff --git a/.github/actions/CreateDockerImage/action.yaml b/.github/actions/CreateDockerImage/action.yaml new file mode 100644 index 0000000..b05ae2a --- /dev/null +++ b/.github/actions/CreateDockerImage/action.yaml @@ -0,0 +1,71 @@ +name: Create Docker Image NSG +description: | + 'Builds a Docker image in the repository' +inputs: + docker_password: + required: true + description: "Docker password" + docker_username: + required: true + description: "Docker username" + docker_owner: + required: true + description: "Docker owner" + docker_args: + description: "Docker build arguments" + required: false + default: "" + tag: + description: "v1.0.0" + required: false + default: "latest" + image_name: + description: "Name of the Docker image" + required: true + source_path: + description: "Path to the Dockerfile" + required: true + +runs: + using: "composite" + steps: + - name: Build args function + shell: bash + id: build_args + run: | + echo "Building args for docker build" + function build_args() { + local args=() + for arg in "${{ inputs.docker_args }}"; do + args+=("--build-arg" "$arg") + done + echo "${args[@]}" + } + echo "args=$(build_args)" >> $GITHUB_OUTPUT + + - name: Build docker images and push it to GitHub Container Registry + shell: bash + run: | + echo "Log in to GitHub Container Registry" + echo "${{ inputs.docker_password }}" | docker login ghcr.io -u ${{ inputs.docker_username }} --password-stdin + REPO_OWNER_LOWER=$(echo "${{ inputs.docker_owner }}" | tr '[:upper:]' '[:lower:]') + + echo "Build ${{ inputs.image_name }} docker image" + docker build \ + ${{ steps.build_args.outputs.args}} \ + -t ghcr.io/$REPO_OWNER_LOWER/${{ inputs.image_name }}:${{ inputs.tag }} ${{ inputs.source_path }} + + echo "Docker image built successfully" + echo "Push ${{ inputs.image_name }} docker image to GitHub Container Registry" + docker push ghcr.io/$REPO_OWNER_LOWER/${{ inputs.image_name }}:${{ inputs.tag }} + + # VITE_BACKEND_API_URL="https://rest-api-$WEBAPP_BACKEND_NAME.azurewebsites.net" + + # echo "Build FE docker image" + # docker build \ + # --build-arg VITE_BACKEND_API_URL=$VITE_BACKEND_API_URL \ + # -t ghcr.io/$REPO_OWNER_LOWER/rest-api-frontend:latest frontend_app + + # echo "Docker image built successfully" + # echo "Push FE docker image to GitHub Container Registry" + # docker push ghcr.io/$REPO_OWNER_LOWER/rest-api-frontend:latest diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index fc3eb5c..6125934 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -63,14 +63,14 @@ jobs: address_prefix=$((address_prefix + 1)) done - - name: Create FE NSG + - name: Create FE NSG uses: ./.github/actions/CreateNSGRule with: AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_FE }} AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} NSG_RULE_NAME: "AllowTraffic4Port" - ALLOWED_PORTS: '80,443' + ALLOWED_PORTS: "80,443" - name: Create BE NSG uses: ./.github/actions/CreateNSGRule @@ -79,17 +79,16 @@ jobs: AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_BE }} AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} NSG_RULE_NAME: ${{ vars.NSG_RULE_NAME }} - ALLOWED_PORTS: '80,443' + ALLOWED_PORTS: "80,443" - - name: Create DB NSG + - name: Create DB NSG uses: ./.github/actions/CreateNSGRule with: AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_DB }} AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} NSG_RULE_NAME: ${{ vars.NSG_RULE_NAME }} - ALLOWED_PORTS: '5432' - + ALLOWED_PORTS: "5432" - name: Create Key Vault run: | @@ -170,32 +169,55 @@ jobs: - name: Checkout code uses: actions/checkout@v2 - - - name: Build docker images and push it to GitHub Container Registry - run: | - echo "Log in to GitHub Container Registry" - echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - REPO_OWNER_LOWER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') - - echo "Build BE docker image" - docker build \ - --build-arg POSTGRES_PASSWORD="${{ env.AZURE-POSTGRESQL-ADMIN-PASSWORD }}" \ - -t ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest backend_app - - echo "Docker image built successfully" - echo "Push BE docker image to GitHub Container Registry" - docker push ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest - - VITE_BACKEND_API_URL="https://rest-api-$WEBAPP_BACKEND_NAME.azurewebsites.net" - - echo "Build FE docker image" - docker build \ - --build-arg VITE_BACKEND_API_URL=$VITE_BACKEND_API_URL \ - -t ghcr.io/$REPO_OWNER_LOWER/rest-api-frontend:latest frontend_app - - echo "Docker image built successfully" - echo "Push FE docker image to GitHub Container Registry" - docker push ghcr.io/$REPO_OWNER_LOWER/rest-api-frontend:latest + - name: Build and push BE Docker image + uses: ./.github/actions/CreateDockerImage + with: + docker_password: ${{ secrets.GITHUB_TOKEN }} + docker_username: ${{ github.actor }} + docker_owner: ${{ github.repository_owner }} + image_name: rest-api-backend + source_path: backend_app + tag: latest + docker_args: | + POSTGRES_PASSWORD="${{ env.AZURE-POSTGRESQL-ADMIN-PASSWORD }}" + + - name: Build and push FE Docker image + uses: ./.github/actions/CreateDockerImage + with: + docker_password: ${{ secrets.GITHUB_TOKEN }} + docker_username: ${{ github.actor }} + docker_owner: ${{ github.repository_owner }} + image_name: rest-api-frontend + source_path: frontend_app + tag: latest + docker_args: | + VITE_BACKEND_API_URL="https://rest-api-$WEBAPP_BACKEND_NAME.azurewebsites.net" + + # - name: Build docker images and push it to GitHub Container Registry + # run: | + # echo "Log in to GitHub Container Registry" + # echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + # REPO_OWNER_LOWER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') + + # echo "Build BE docker image" + # docker build \ + # --build-arg POSTGRES_PASSWORD="${{ env.AZURE-POSTGRESQL-ADMIN-PASSWORD }}" \ + # -t ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest backend_app + + # echo "Docker image built successfully" + # echo "Push BE docker image to GitHub Container Registry" + # docker push ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest + + # VITE_BACKEND_API_URL="https://rest-api-$WEBAPP_BACKEND_NAME.azurewebsites.net" + + # echo "Build FE docker image" + # docker build \ + # --build-arg VITE_BACKEND_API_URL=$VITE_BACKEND_API_URL \ + # -t ghcr.io/$REPO_OWNER_LOWER/rest-api-frontend:latest frontend_app + + # echo "Docker image built successfully" + # echo "Push FE docker image to GitHub Container Registry" + # docker push ghcr.io/$REPO_OWNER_LOWER/rest-api-frontend:latest create_webapp: name: Deploy Web Apps From 58832a0c407f6aaf52634d0271fc02a85f284a8b Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Thu, 7 Aug 2025 22:45:37 +0200 Subject: [PATCH 30/62] fix: standardize formatting for Azure location and NSG rule parameters in CI/CD workflow --- .github/workflows/cicd.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 4654169..5b6adc2 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -77,7 +77,7 @@ jobs: with: AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_BE }} - AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} + AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} ALLOWED_PORTS: "80,443" NSG_RULE_NAME: "AllowTraffic4Port" - name: Create DB NSG @@ -86,11 +86,8 @@ jobs: AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_DB }} AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} - NSG_RULE_NAME: ${{ vars.NSG_RULE_NAME }} - ALLOWED_PORTS: "5432" NSG_RULE_NAME: "AllowTraffic4Port" - ALLOWED_PORTS: '5432' - + ALLOWED_PORTS: "5432" - name: Create Key Vault run: | From 50f6efe438bdb5ac851d536665f17c1c445b88a1 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Thu, 7 Aug 2025 22:46:21 +0200 Subject: [PATCH 31/62] no need any additional ports for be and db --- .github/actions/CreateNSGRule/action.yaml | 2 +- .github/workflows/cicd.yaml | 28 ++++++++++++++++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/.github/actions/CreateNSGRule/action.yaml b/.github/actions/CreateNSGRule/action.yaml index e6db112..ef7157f 100644 --- a/.github/actions/CreateNSGRule/action.yaml +++ b/.github/actions/CreateNSGRule/action.yaml @@ -22,7 +22,7 @@ inputs: description: 'Comma-separated list of allowed ports' required: false type: string - default: '80,443' + default: '' runs: using: composite diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 430fe74..8c97fe0 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -52,7 +52,23 @@ jobs: --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ --vnet-name ${{ vars.AZURE_VNET_NAME }} \ --address-prefixes 10.0.$address_prefix.0/24 \ - --delegations Microsoft.Web/serverFarms + + # Define subnet delegation mapping + declare -A subnet_delegations + subnet_delegations["${{ vars.AZURE_SUBNET_BE }}"]="Microsoft.Web/serverFarms" + subnet_delegations["${{ vars.AZURE_SUBNET_FE }}"]="Microsoft.Web/serverFarms" + subnet_delegations["${{ vars.AZURE_SUBNET_DB }}"]="Microsoft.DBforPostgreSQL/flexibleServers" + + for subnet in ${{ vars.AZURE_SUBNET_BE }} ${{ vars.AZURE_SUBNET_FE }} ${{ vars.AZURE_SUBNET_DB }} ; do + echo "Creating Subnet for $subnet..." + delegation="${subnet_delegations[$subnet]}" + if [[ -n "$delegation" ]]; then + az network vnet subnet create \ + --name $subnet \ + --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ + --vnet-name ${{ vars.AZURE_VNET_NAME }} \ + --address-prefixes 10.0.$address_prefix.0/24 \ + --delegations $delegation else az network vnet subnet create \ --name $subnet \ @@ -63,6 +79,7 @@ jobs: address_prefix=$((address_prefix + 1)) done + # Allow 80 and 443 ports fro, - name: Create FE NSG uses: ./.github/actions/CreateNSGRule with: @@ -72,24 +89,23 @@ jobs: NSG_RULE_NAME: "AllowTraffic4Port" ALLOWED_PORTS: '80,443' + # No need any specific rules for BE NSG + # Needed functionality covered by default rules - name: Create BE NSG uses: ./.github/actions/CreateNSGRule with: AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_BE }} AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} - NSG_RULE_NAME: "AllowTraffic4Port" - ALLOWED_PORTS: '80,443' + # No need any specific rules for BE NSG + # Needed functionality covered by default rules - name: Create DB NSG uses: ./.github/actions/CreateNSGRule with: AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_DB }} AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} - NSG_RULE_NAME: "AllowTraffic4Port" - ALLOWED_PORTS: '5432' - - name: Create Key Vault run: | From f13309ec9c08bb5ab0b54a6956f861edee724db7 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Thu, 7 Aug 2025 23:06:59 +0200 Subject: [PATCH 32/62] refactor: remove redundant subnet creation logic in CI/CD workflow --- .github/workflows/cicd.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 4d70981..0a77e96 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -44,14 +44,6 @@ jobs: --address-prefixes 10.0.0.0/16 fi address_prefix=1 - for subnet in ${{ vars.AZURE_SUBNET_BE }} ${{ vars.AZURE_SUBNET_FE }} ${{ vars.AZURE_SUBNET_DB }} ; do - echo "Creating Subnet for $subnet..." - if [[ "$subnet" == "${{ vars.AZURE_SUBNET_BE }}" ]] || [[ "$subnet" == "${{ vars.AZURE_SUBNET_FE }}" ]]; then - az network vnet subnet create \ - --name $subnet \ - --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ - --vnet-name ${{ vars.AZURE_VNET_NAME }} \ - --address-prefixes 10.0.$address_prefix.0/24 \ # Define subnet delegation mapping declare -A subnet_delegations From 5dbfb08e2190b7dbeb4b156360f013b1d86a4bdc Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Tue, 12 Aug 2025 22:36:52 +0200 Subject: [PATCH 33/62] feat: add CreateWebApp action for deploying Azure Web Apps with VNet integration and logging --- .github/actions/CreateNSGRule/action.yaml | 76 +++++++++++------------ .github/actions/CreateWebAPP/action.yaml | 70 +++++++++++++++++++++ 2 files changed, 108 insertions(+), 38 deletions(-) create mode 100644 .github/actions/CreateWebAPP/action.yaml diff --git a/.github/actions/CreateNSGRule/action.yaml b/.github/actions/CreateNSGRule/action.yaml index e6db112..da51928 100644 --- a/.github/actions/CreateNSGRule/action.yaml +++ b/.github/actions/CreateNSGRule/action.yaml @@ -2,27 +2,27 @@ name: Create NSG Rule inputs: AZURE_RESOURCE_GROUP: - description: 'Azure Resource Group Name' + description: "Azure Resource Group Name" required: true type: string AZURE_NSG_NAME: - description: 'Azure NSG Name' + description: "Azure NSG Name" required: true type: string AZURE_LOCATION: - description: 'Azure Location' + description: "Azure Location" required: true type: string NSG_RULE_NAME: - description: 'Azure NSG Rule Name' + description: "Azure NSG Rule Name" required: false type: string - default: 'AllowTraffic' + default: "AllowTraffic" ALLOWED_PORTS: - description: 'Comma-separated list of allowed ports' + description: "Comma-separated list of allowed ports" required: false type: string - default: '80,443' + default: "80,443" runs: using: composite @@ -30,37 +30,37 @@ runs: - name: Create ${{ inputs.AZURE_NSG_NAME }} NSG Rule Port shell: bash run: | - echo "Checking if '${{ inputs.AZURE_NSG_NAME }}' NSG for VNet exists..." - if az network nsg show --name ${{ inputs.AZURE_NSG_NAME }} --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }}; then - echo "NSG already exists." + echo "Checking if '${{ inputs.AZURE_NSG_NAME }}' NSG for VNet exists..." + if az network nsg show --name ${{ inputs.AZURE_NSG_NAME }} --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }}; then + echo "NSG already exists." + else + echo "Creating NSG for VNet..." + az network nsg create \ + --name ${{ inputs.AZURE_NSG_NAME }} \ + --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} \ + --location ${{ inputs.AZURE_LOCATION }} + fi + + PORTS=$(echo ${{ inputs.ALLOWED_PORTS }} | tr ',' ' ') + PRIO=1000 + for PORT in $PORTS; do + echo "Checking if NSG rule for port $PORT exists..." + if az network nsg rule show --nsg-name ${{ inputs.AZURE_NSG_NAME }} --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} --name "${{ inputs.NSG_RULE_NAME}}-$PORT-pr-$PRIO"; then + echo "NSG rule for port $PORT already exists." else - echo "Creating NSG for VNet..." - az network nsg create \ - --name ${{ inputs.AZURE_NSG_NAME }} \ + echo "Creating NSG rule for port $PORT..." + az network nsg rule create \ + --nsg-name ${{ inputs.AZURE_NSG_NAME }} \ --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} \ - --location ${{ inputs.AZURE_LOCATION }} + --name ${{ inputs.NSG_RULE_NAME}}-$PORT-pr-$PRIO \ + --priority $PRIO \ + --direction Inbound \ + --access Allow \ + --protocol Tcp \ + --source-address-prefixes '*' \ + --source-port-ranges '*' \ + --destination-address-prefixes '*' \ + --destination-port-ranges $PORT fi - - PORTS=$(echo ${{ inputs.ALLOWED_PORTS }} | tr ',' ' ') - PRIO=1000 - for PORT in $PORTS; do - echo "Checking if NSG rule for port $PORT exists..." - if az network nsg rule show --nsg-name ${{ inputs.AZURE_NSG_NAME }} --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} --name "${{ inputs.NSG_RULE_NAME}}-$PORT-pr-$PRIO"; then - echo "NSG rule for port $PORT already exists." - else - echo "Creating NSG rule for port $PORT..." - az network nsg rule create \ - --nsg-name ${{ inputs.AZURE_NSG_NAME }} \ - --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} \ - --name ${{ inputs.NSG_RULE_NAME}}-$PORT-pr-$PRIO \ - --priority $PRIO \ - --direction Inbound \ - --access Allow \ - --protocol Tcp \ - --source-address-prefixes '*' \ - --source-port-ranges '*' \ - --destination-address-prefixes '*' \ - --destination-port-ranges $PORT - fi - PRIO=$((PRIO + 100)) - done + PRIO=$((PRIO + 100)) + done diff --git a/.github/actions/CreateWebAPP/action.yaml b/.github/actions/CreateWebAPP/action.yaml new file mode 100644 index 0000000..9fe3a7d --- /dev/null +++ b/.github/actions/CreateWebAPP/action.yaml @@ -0,0 +1,70 @@ +name: Create WebApp + +inputs: + WEBAPP_NAME: + description: "Name of the web app" + required: true + type: string + AZURE_APP_SERVICE_PLAN_NAME: + description: "Azure App Service Plan name" + required: true + type: string + AZURE_RESOURCE_GROUP: + description: "Azure Resource Group Name" + required: true + type: string + WEBAPP_VNET_NAME: + description: "Azure Virtual Network Name" + required: true + type: string + WEBAPP_SUBNET: + description: "Azure Subnet Name for the web app" + required: true + type: string + CONTAINER_IMAGE_NAME: + description: "Container image name in GitHub Container Registry" + required: true + type: string + WEBAPP_SETTINGS: + description: "Environment variables for the web app in key=value format(space separated)" + required: false + type: string + default: "" + +runs: + using: composite + steps: + - name: Create WebApp + shell: pwsh + run: | + echo "Creating Web App..." + az webapp create ` + --name ${{ inputs.WEBAPP_NAME }} ` + --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} ` + --plan ${{ inputs.AZURE_APP_SERVICE_PLAN_NAME }} ` + --container-registry-url ghcr.io ` + --container-image-name ${{ inputs.CONTAINER_IMAGE_NAME }} + + if ('${{ inputs.WEBAPP_SETTINGS }}' -match '^(\w+=\w+ ?)+$') { + echo "Set environment variables for ${{ inputs.WEBAPP_NAME }}" + az webapp config appsettings set ` + --name ${{ inputs.WEBAPP_NAME }} ` + --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} ` + --settings ${{ inputs.WEBAPP_SETTINGS }} + } + else { + echo "No environment variables to set." + } + + echo "Enable logging for backend web app" + az webapp log config ` + --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} ` + --name ${{ inputs.WEBAPP_NAME }} ` + --docker-container-logging filesystem + + echo "VNet integration for backend web app" + az webapp vnet-integration add ` + --name ${{ inputs.WEBAPP_NAME }} ` + --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} ` + --vnet "${{ inputs.WEBAPP_VNET_NAME }}" ` + --subnet "${{ inputs.WEBAPP_SUBNET }}" From 1f3bbb8e0a7ca8987a588783af4da3bd2b74f0ea Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Wed, 13 Aug 2025 21:06:35 +0200 Subject: [PATCH 34/62] feat: update CreateWebApp action to use lowercased container image name and streamline web app creation in CI/CD workflow --- .github/actions/CreateWebAPP/action.yaml | 4 +- .github/workflows/cicd.yaml | 117 +++++------------------ 2 files changed, 25 insertions(+), 96 deletions(-) diff --git a/.github/actions/CreateWebAPP/action.yaml b/.github/actions/CreateWebAPP/action.yaml index 9fe3a7d..950637e 100644 --- a/.github/actions/CreateWebAPP/action.yaml +++ b/.github/actions/CreateWebAPP/action.yaml @@ -25,6 +25,7 @@ inputs: description: "Container image name in GitHub Container Registry" required: true type: string + WEBAPP_SETTINGS: description: "Environment variables for the web app in key=value format(space separated)" required: false @@ -37,13 +38,14 @@ runs: - name: Create WebApp shell: pwsh run: | + $CONTAINER_IMAGE_NAME="${{ inputs.CONTAINER_IMAGE_NAME }}".tolower() echo "Creating Web App..." az webapp create ` --name ${{ inputs.WEBAPP_NAME }} ` --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} ` --plan ${{ inputs.AZURE_APP_SERVICE_PLAN_NAME }} ` --container-registry-url ghcr.io ` - --container-image-name ${{ inputs.CONTAINER_IMAGE_NAME }} + --container-image-name $CONTAINER_IMAGE_NAME if ('${{ inputs.WEBAPP_SETTINGS }}' -match '^(\w+=\w+ ?)+$') { echo "Set environment variables for ${{ inputs.WEBAPP_NAME }}" diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 0a77e96..0454131 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -50,7 +50,7 @@ jobs: subnet_delegations["${{ vars.AZURE_SUBNET_BE }}"]="Microsoft.Web/serverFarms" subnet_delegations["${{ vars.AZURE_SUBNET_FE }}"]="Microsoft.Web/serverFarms" subnet_delegations["${{ vars.AZURE_SUBNET_DB }}"]="Microsoft.DBforPostgreSQL/flexibleServers" - + for subnet in ${{ vars.AZURE_SUBNET_BE }} ${{ vars.AZURE_SUBNET_FE }} ${{ vars.AZURE_SUBNET_DB }} ; do echo "Creating Subnet for $subnet..." delegation="${subnet_delegations[$subnet]}" @@ -72,7 +72,7 @@ jobs: done # Allow 80 and 443 ports fro, - - name: Create FE NSG + - name: Create FE NSG uses: ./.github/actions/CreateNSGRule with: AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} @@ -92,7 +92,7 @@ jobs: # No need any specific rules for BE NSG # Needed functionality covered by default rules - - name: Create DB NSG + - name: Create DB NSG uses: ./.github/actions/CreateNSGRule with: AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} @@ -202,38 +202,11 @@ jobs: docker_args: | VITE_BACKEND_API_URL="https://rest-api-$WEBAPP_BACKEND_NAME.azurewebsites.net" - # - name: Build docker images and push it to GitHub Container Registry - # run: | - # echo "Log in to GitHub Container Registry" - # echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - # REPO_OWNER_LOWER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') - - # echo "Build BE docker image" - # docker build \ - # --build-arg POSTGRES_PASSWORD="${{ env.AZURE-POSTGRESQL-ADMIN-PASSWORD }}" \ - # -t ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest backend_app - - # echo "Docker image built successfully" - # echo "Push BE docker image to GitHub Container Registry" - # docker push ghcr.io/$REPO_OWNER_LOWER/rest-api-backend:latest - - # VITE_BACKEND_API_URL="https://rest-api-$WEBAPP_BACKEND_NAME.azurewebsites.net" - - # echo "Build FE docker image" - # docker build \ - # --build-arg VITE_BACKEND_API_URL=$VITE_BACKEND_API_URL \ - # -t ghcr.io/$REPO_OWNER_LOWER/rest-api-frontend:latest frontend_app - - # echo "Docker image built successfully" - # echo "Push FE docker image to GitHub Container Registry" - # docker push ghcr.io/$REPO_OWNER_LOWER/rest-api-frontend:latest - - create_webapp: + deploy_web_apps: name: Deploy Web Apps runs-on: ubuntu-latest environment: dev needs: [build_docker_image] - steps: - name: Login to Azure uses: azure/login@v2 @@ -255,71 +228,25 @@ jobs: --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ --sku B2 \ --is-linux - fi - name: Create backend_app - run: | - BACKEND_WEBAPP_NAME="rest-api-${{ vars.WEBAPP_BACKEND_NAME }}" - PG_SERVER_NAME="${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}.postgres.database.azure.com" - - REPO_OWNER_LOWER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') - echo "Creating Web App..." - az webapp create \ - --name $BACKEND_WEBAPP_NAME \ - --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ - --plan ${{ vars.AZURE_APP_SERVICE_PLAN_NAME }} \ - --container-registry-url ghcr.io \ - --container-image-name $REPO_OWNER_LOWER/rest-api-backend:latest - - echo "Set environment variables for backend web app" - az webapp config appsettings set \ - --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ - --name $BACKEND_WEBAPP_NAME \ - --settings \ - POSTGRES_HOST="$PG_SERVER_NAME" \ - POSTGRES_USER="${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" \ - POSTGRES_PASSWORD="${{ env.AZURE-POSTGRESQL-ADMIN-PASSWORD }}" - - echo "Enable logging for backend web app" - az webapp log config \ - --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ - --name $BACKEND_WEBAPP_NAME \ - --docker-container-logging filesystem - - echo "VNet integration for backend web app" - az webapp vnet-integration add \ - --name "$BACKEND_WEBAPP_NAME" \ - --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ - --vnet "${{ vars.AZURE_VNET_NAME }}" \ - --subnet "${{ vars.AZURE_SUBNET_BE }}" + uses: ./.github/actions/CreateWebAPP + with: + WEBAPP_NAME: "rest-api-${{ vars.WEBAPP_BACKEND_NAME }}" + AZURE_APP_SERVICE_PLAN_NAME: ${{ vars.AZURE_APP_SERVICE_PLAN_NAME }} + AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} + WEBAPP_VNET_NAME: ${{ vars.AZURE_VNET_NAME }} + WEBAPP_SUBNET: ${{ vars.AZURE_SUBNET_BE }} + CONTAINER_IMAGE_NAME: "${{ github.repository_owner }}/rest-api-backend:latest" + WEBAPP_SETTINGS: "POSTGRES_HOST='${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}.postgres.database.azure.com' POSTGRES_USER='${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}' POSTGRES_PASSWORD='${{ env.AZURE-POSTGRESQL-ADMIN-PASSWORD }}'" - name: Create frontend_app - run: | - FRONTEND_WEBAPP_NAME="rest-api-${{ vars.WEBAPP_FRONTEND_NAME }}" - REPO_OWNER_LOWER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') - echo "Creating Web App..." - az webapp create \ - --name "$FRONTEND_WEBAPP_NAME" \ - --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ - --plan ${{ vars.AZURE_APP_SERVICE_PLAN_NAME }} \ - --container-registry-url ghcr.io \ - --container-image-name $REPO_OWNER_LOWER/rest-api-frontend:latest - - echo "Configuring frontend_app settings..." - az webapp config appsettings set \ - --name $FRONTEND_WEBAPP_NAME \ - --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ - --settings "VITE_BACKEND_API_URL=https://rest-api-${{ vars.WEBAPP_BACKEND_NAME }}.azurewebsites.net" - - echo "Enable logging for frontend web app" - az webapp log config \ - --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ - --name $FRONTEND_WEBAPP_NAME \ - --docker-container-logging filesystem - - echo "VNet integration for frontend web app" - az webapp vnet-integration add \ - --name "$FRONTEND_WEBAPP_NAME" \ - --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ - --vnet "${{ vars.AZURE_VNET_NAME }}" \ - --subnet "${{ vars.AZURE_SUBNET_FE }}" + uses: ./.github/actions/CreateWebAPP + with: + WEBAPP_NAME: "rest-api-${{ vars.WEBAPP_FRONTEND_NAME }}" + AZURE_APP_SERVICE_PLAN_NAME: ${{ vars.AZURE_APP_SERVICE_PLAN_NAME }} + AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} + WEBAPP_VNET_NAME: ${{ vars.AZURE_VNET_NAME }} + WEBAPP_SUBNET: ${{ vars.AZURE_SUBNET_FE }} + CONTAINER_IMAGE_NAME: "${{ github.repository_owner }}/rest-api-frontend:latest" + WEBAPP_SETTINGS: "VITE_BACKEND_API_URL='https://rest-api-${{ vars.WEBAPP_BACKEND_NAME }}.azurewebsites.net'" From e0903050c7216b60eb7baa16ad36352efb02127f Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Wed, 13 Aug 2025 21:24:03 +0200 Subject: [PATCH 35/62] fix: add missing conditional closure for backend app creation in CI/CD workflow --- .github/workflows/cicd.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 0454131..772ded6 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -228,6 +228,7 @@ jobs: --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ --sku B2 \ --is-linux + fi - name: Create backend_app uses: ./.github/actions/CreateWebAPP From db199dd90155e60fbf512d00ab22585fbdd6af01 Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Wed, 13 Aug 2025 21:31:12 +0200 Subject: [PATCH 36/62] feat: add code checkout step before Azure login in deploy_web_apps job --- .github/workflows/cicd.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 772ded6..ca13e94 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -208,6 +208,8 @@ jobs: environment: dev needs: [build_docker_image] steps: + - name: Checkout code + uses: actions/checkout@v4 - name: Login to Azure uses: azure/login@v2 with: From 42d1b108b90b36ec3ea52cbf9c1b35addab2643d Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Wed, 13 Aug 2025 21:38:25 +0200 Subject: [PATCH 37/62] fix: update backend app settings with hardcoded PostgreSQL password for deployment --- .github/workflows/cicd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index ca13e94..ac1d25b 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -241,7 +241,7 @@ jobs: WEBAPP_VNET_NAME: ${{ vars.AZURE_VNET_NAME }} WEBAPP_SUBNET: ${{ vars.AZURE_SUBNET_BE }} CONTAINER_IMAGE_NAME: "${{ github.repository_owner }}/rest-api-backend:latest" - WEBAPP_SETTINGS: "POSTGRES_HOST='${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}.postgres.database.azure.com' POSTGRES_USER='${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}' POSTGRES_PASSWORD='${{ env.AZURE-POSTGRESQL-ADMIN-PASSWORD }}'" + WEBAPP_SETTINGS: "POSTGRES_HOST='${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}.postgres.database.azure.com' POSTGRES_USER='${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}' POSTGRES_PASSWORD='1223wwsd'" - name: Create frontend_app uses: ./.github/actions/CreateWebAPP From b73f63e476b78f3511562f8c3662e646ba779ea9 Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Wed, 13 Aug 2025 21:48:37 +0200 Subject: [PATCH 38/62] fix: update WEBAPP_SETTINGS syntax for backend and frontend app creation --- .github/workflows/cicd.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index ac1d25b..58ca870 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -241,8 +241,7 @@ jobs: WEBAPP_VNET_NAME: ${{ vars.AZURE_VNET_NAME }} WEBAPP_SUBNET: ${{ vars.AZURE_SUBNET_BE }} CONTAINER_IMAGE_NAME: "${{ github.repository_owner }}/rest-api-backend:latest" - WEBAPP_SETTINGS: "POSTGRES_HOST='${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}.postgres.database.azure.com' POSTGRES_USER='${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}' POSTGRES_PASSWORD='1223wwsd'" - + WEBAPP_SETTINGS: 'POSTGRES_HOST="${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}.postgres.database.azure.com" POSTGRES_USER="${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" POSTGRES_PASSWORD="1223wwsd"' - name: Create frontend_app uses: ./.github/actions/CreateWebAPP with: @@ -252,4 +251,4 @@ jobs: WEBAPP_VNET_NAME: ${{ vars.AZURE_VNET_NAME }} WEBAPP_SUBNET: ${{ vars.AZURE_SUBNET_FE }} CONTAINER_IMAGE_NAME: "${{ github.repository_owner }}/rest-api-frontend:latest" - WEBAPP_SETTINGS: "VITE_BACKEND_API_URL='https://rest-api-${{ vars.WEBAPP_BACKEND_NAME }}.azurewebsites.net'" + WEBAPP_SETTINGS: 'VITE_BACKEND_API_URL="https://rest-api-${{ vars.WEBAPP_BACKEND_NAME }}.azurewebsites.net"' From ca719251b9b5db3be3fd973fd320dcd93b11ad91 Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Thu, 14 Aug 2025 21:12:30 +0200 Subject: [PATCH 39/62] fix: update Docker build arguments for backend app in CI/CD workflow --- .github/actions/CreateDockerImage/action.yaml | 37 +++++++------------ .github/workflows/cicd.yaml | 4 +- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/.github/actions/CreateDockerImage/action.yaml b/.github/actions/CreateDockerImage/action.yaml index b05ae2a..34487e0 100644 --- a/.github/actions/CreateDockerImage/action.yaml +++ b/.github/actions/CreateDockerImage/action.yaml @@ -12,7 +12,7 @@ inputs: required: true description: "Docker owner" docker_args: - description: "Docker build arguments" + description: "Docker build arguments" # e.g. "FOO=bar BAZ=qux" required: false default: "" tag: @@ -30,42 +30,31 @@ runs: using: "composite" steps: - name: Build args function - shell: bash + shell: pwsh id: build_args run: | echo "Building args for docker build" - function build_args() { - local args=() - for arg in "${{ inputs.docker_args }}"; do - args+=("--build-arg" "$arg") - done - echo "${args[@]}" + $input_args = "${{ inputs.docker_args }}" # e.g. "FOO=bar BAZ=qux" + $arr_ = @() + foreach ($input_arg in $input_args -split '\s+') { + $arr_ += "--build-arg $input_arg" } - echo "args=$(build_args)" >> $GITHUB_OUTPUT + $joinedArgs = $arr_ -join ' ' # single space-separated string + echo "args=$joinedArgs" >> $env:GITHUB_OUTPUT - name: Build docker images and push it to GitHub Container Registry shell: bash run: | echo "Log in to GitHub Container Registry" - echo "${{ inputs.docker_password }}" | docker login ghcr.io -u ${{ inputs.docker_username }} --password-stdin + echo "${{ inputs.docker_password }}" | docker login ghcr.io -u "${{ inputs.docker_username }}" --password-stdin REPO_OWNER_LOWER=$(echo "${{ inputs.docker_owner }}" | tr '[:upper:]' '[:lower:]') echo "Build ${{ inputs.image_name }} docker image" docker build \ - ${{ steps.build_args.outputs.args}} \ - -t ghcr.io/$REPO_OWNER_LOWER/${{ inputs.image_name }}:${{ inputs.tag }} ${{ inputs.source_path }} + ${ { steps.build_args.outputs.args } } \ + -t ghcr.io/$REPO_OWNER_LOWER/${{ inputs.image_name }}:${{ inputs.tag }} \ + "${{ inputs.source_path }}" echo "Docker image built successfully" echo "Push ${{ inputs.image_name }} docker image to GitHub Container Registry" - docker push ghcr.io/$REPO_OWNER_LOWER/${{ inputs.image_name }}:${{ inputs.tag }} - - # VITE_BACKEND_API_URL="https://rest-api-$WEBAPP_BACKEND_NAME.azurewebsites.net" - - # echo "Build FE docker image" - # docker build \ - # --build-arg VITE_BACKEND_API_URL=$VITE_BACKEND_API_URL \ - # -t ghcr.io/$REPO_OWNER_LOWER/rest-api-frontend:latest frontend_app - - # echo "Docker image built successfully" - # echo "Push FE docker image to GitHub Container Registry" - # docker push ghcr.io/$REPO_OWNER_LOWER/rest-api-frontend:latest + docker push ghcr.io/$REPO_OWNER_LOWER/${{ inputs.image_name }}:${{ inputs.tag }}" diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 58ca870..b187cea 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -188,7 +188,9 @@ jobs: source_path: backend_app tag: latest docker_args: | - POSTGRES_PASSWORD="${{ env.AZURE-POSTGRESQL-ADMIN-PASSWORD }}" + POSTGRES_PASSWORD="11qewewew" + POSTGRES_HOST="${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}.postgres.database.azure.com" + POSTGRES_USER="${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" - name: Build and push FE Docker image uses: ./.github/actions/CreateDockerImage From 7c938ae12c9fc6d372006bb335ea418c450f8e96 Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Thu, 14 Aug 2025 21:28:40 +0200 Subject: [PATCH 40/62] fix: correct string interpolation syntax for docker_args in action.yaml --- .github/actions/CreateDockerImage/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/CreateDockerImage/action.yaml b/.github/actions/CreateDockerImage/action.yaml index 34487e0..7d7af2f 100644 --- a/.github/actions/CreateDockerImage/action.yaml +++ b/.github/actions/CreateDockerImage/action.yaml @@ -34,7 +34,7 @@ runs: id: build_args run: | echo "Building args for docker build" - $input_args = "${{ inputs.docker_args }}" # e.g. "FOO=bar BAZ=qux" + $input_args = '${{ inputs.docker_args }}' # e.g. "FOO=bar BAZ=qux" $arr_ = @() foreach ($input_arg in $input_args -split '\s+') { $arr_ += "--build-arg $input_arg" From 4beff7a39a280269ce322683cdf8d02212fff491 Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Thu, 14 Aug 2025 21:33:29 +0200 Subject: [PATCH 41/62] fix: correct syntax for accessing build args in Docker build command --- .github/actions/CreateDockerImage/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/CreateDockerImage/action.yaml b/.github/actions/CreateDockerImage/action.yaml index 7d7af2f..3384140 100644 --- a/.github/actions/CreateDockerImage/action.yaml +++ b/.github/actions/CreateDockerImage/action.yaml @@ -51,7 +51,7 @@ runs: echo "Build ${{ inputs.image_name }} docker image" docker build \ - ${ { steps.build_args.outputs.args } } \ + ${{ steps.build_args.outputs.args }} \ -t ghcr.io/$REPO_OWNER_LOWER/${{ inputs.image_name }}:${{ inputs.tag }} \ "${{ inputs.source_path }}" From 550ef9ee8751a5eea7da49f4ed1d15e28f8b8180 Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Thu, 14 Aug 2025 21:39:35 +0200 Subject: [PATCH 42/62] fix: add check for empty build arguments in Docker build step --- .github/actions/CreateDockerImage/action.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/actions/CreateDockerImage/action.yaml b/.github/actions/CreateDockerImage/action.yaml index 3384140..3aea82e 100644 --- a/.github/actions/CreateDockerImage/action.yaml +++ b/.github/actions/CreateDockerImage/action.yaml @@ -36,8 +36,10 @@ runs: echo "Building args for docker build" $input_args = '${{ inputs.docker_args }}' # e.g. "FOO=bar BAZ=qux" $arr_ = @() - foreach ($input_arg in $input_args -split '\s+') { - $arr_ += "--build-arg $input_arg" + foreach ($input_arg in $input_args -split '\s+') { + if (-not [string]::IsNullOrWhiteSpace($input_arg)) { + $arr_ += "--build-arg $input_arg" + } } $joinedArgs = $arr_ -join ' ' # single space-separated string echo "args=$joinedArgs" >> $env:GITHUB_OUTPUT From 1f4d292a89ee3065b854c8ebc3c7f66954559c94 Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Thu, 14 Aug 2025 21:44:22 +0200 Subject: [PATCH 43/62] fix: remove extraneous quotation mark in docker push command --- .github/actions/CreateDockerImage/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/CreateDockerImage/action.yaml b/.github/actions/CreateDockerImage/action.yaml index 3aea82e..ce87171 100644 --- a/.github/actions/CreateDockerImage/action.yaml +++ b/.github/actions/CreateDockerImage/action.yaml @@ -59,4 +59,4 @@ runs: echo "Docker image built successfully" echo "Push ${{ inputs.image_name }} docker image to GitHub Container Registry" - docker push ghcr.io/$REPO_OWNER_LOWER/${{ inputs.image_name }}:${{ inputs.tag }}" + docker push ghcr.io/$REPO_OWNER_LOWER/${{ inputs.image_name }}:${{ inputs.tag }} From b23487fbe0a6411db712dc57922c8b4ca1183acc Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Thu, 14 Aug 2025 22:01:25 +0200 Subject: [PATCH 44/62] fix: correct regex for matching environment variable format in action.yaml --- .github/actions/CreateWebAPP/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/CreateWebAPP/action.yaml b/.github/actions/CreateWebAPP/action.yaml index 950637e..a70b01e 100644 --- a/.github/actions/CreateWebAPP/action.yaml +++ b/.github/actions/CreateWebAPP/action.yaml @@ -47,7 +47,7 @@ runs: --container-registry-url ghcr.io ` --container-image-name $CONTAINER_IMAGE_NAME - if ('${{ inputs.WEBAPP_SETTINGS }}' -match '^(\w+=\w+ ?)+$') { + if ('${{ inputs.WEBAPP_SETTINGS }}' -match '^(\w+="[^"]+" ?)+$') { echo "Set environment variables for ${{ inputs.WEBAPP_NAME }}" az webapp config appsettings set ` --name ${{ inputs.WEBAPP_NAME }} ` From 75cdcbd47cba6bee34604972238796fa1a0cbe60 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Tue, 26 Aug 2025 22:38:42 +0200 Subject: [PATCH 45/62] fix: add managed identity permissions for BackendApp and update WEBAPP_SETTINGS to use Key Vault secret --- .github/actions/CreateWebAPP/action.yaml | 13 ++++++++++++- .github/workflows/cicd.yaml | 14 +++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/actions/CreateWebAPP/action.yaml b/.github/actions/CreateWebAPP/action.yaml index a70b01e..195fdab 100644 --- a/.github/actions/CreateWebAPP/action.yaml +++ b/.github/actions/CreateWebAPP/action.yaml @@ -25,12 +25,16 @@ inputs: description: "Container image name in GitHub Container Registry" required: true type: string - WEBAPP_SETTINGS: description: "Environment variables for the web app in key=value format(space separated)" required: false type: string default: "" + ENABLE_APP_IDENTITY: + description: Enable Managed Identity for the web app + required: false + type: boolean + default: false runs: using: composite @@ -70,3 +74,10 @@ runs: --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} ` --vnet "${{ inputs.WEBAPP_VNET_NAME }}" ` --subnet "${{ inputs.WEBAPP_SUBNET }}" + + if (${{ inputs.ENABLE_APP_IDENTITY }}) { + echo "Enable Managed Identity for backend web app" + az webapp identity assign ` + --name ${{ inputs.WEBAPP_NAME }} ` + --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} + } diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index b187cea..cb5a3c9 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -243,7 +243,19 @@ jobs: WEBAPP_VNET_NAME: ${{ vars.AZURE_VNET_NAME }} WEBAPP_SUBNET: ${{ vars.AZURE_SUBNET_BE }} CONTAINER_IMAGE_NAME: "${{ github.repository_owner }}/rest-api-backend:latest" - WEBAPP_SETTINGS: 'POSTGRES_HOST="${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}.postgres.database.azure.com" POSTGRES_USER="${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" POSTGRES_PASSWORD="1223wwsd"' + ENABLE_APP_IDENTITY: true + WEBAPP_SETTINGS: 'POSTGRES_HOST="${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}.postgres.database.azure.com" POSTGRES_USER="${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" POSTGRES_PASSWORD="@Microsoft.KeyVault(SecretUri=https://${{ vars.AZURE_KEY_VAULT_NAME }}.vault.azure.net/secrets/AZURE-POSTGRESQL-ADMIN-PASSWORD)"' + + - name: Grant permissions to the BackendApp managed identity + run: | + APP_PRINCIPAL_ID=$(az webapp identity show -g "${{ vars.AZURE_RESOURCE_GROUP }}" -n "$BACKEND_WEBAPP_NAME" --query principalId -o tsv) + echo "Granting Key Vault access to Backend APP ($APP_PRINCIPAL_ID)" + az role assignment create \ + --assignee-object-id "$APP_PRINCIPAL_ID" \ + --assignee-principal-type ServicePrincipal \ + --role "Key Vault Secrets User" \ + --scope /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ secrets.AZURE_RESOURCE_GROUP }}/providers/Microsoft.Web/sites/rest-api-${{ vars.WEBAPP_BACKEND_NAME }} + - name: Create frontend_app uses: ./.github/actions/CreateWebAPP with: From 51098d31332894570c07895dc13ef6c3f93e9ed6 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Tue, 26 Aug 2025 23:31:06 +0200 Subject: [PATCH 46/62] fix: update echo statements to include web app name for clarity --- .github/actions/CreateWebAPP/action.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/actions/CreateWebAPP/action.yaml b/.github/actions/CreateWebAPP/action.yaml index 195fdab..bd8d58d 100644 --- a/.github/actions/CreateWebAPP/action.yaml +++ b/.github/actions/CreateWebAPP/action.yaml @@ -43,7 +43,7 @@ runs: shell: pwsh run: | $CONTAINER_IMAGE_NAME="${{ inputs.CONTAINER_IMAGE_NAME }}".tolower() - echo "Creating Web App..." + echo "Creating ${{ inputs.WEBAPP_NAME }} Web App..." az webapp create ` --name ${{ inputs.WEBAPP_NAME }} ` --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} ` @@ -62,21 +62,21 @@ runs: echo "No environment variables to set." } - echo "Enable logging for backend web app" + echo "Enable logging for ${{ inputs.WEBAPP_NAME }} web app" az webapp log config ` --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} ` --name ${{ inputs.WEBAPP_NAME }} ` --docker-container-logging filesystem - echo "VNet integration for backend web app" + echo "VNet integration for ${{ inputs.WEBAPP_NAME }} web app" az webapp vnet-integration add ` --name ${{ inputs.WEBAPP_NAME }} ` --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} ` --vnet "${{ inputs.WEBAPP_VNET_NAME }}" ` --subnet "${{ inputs.WEBAPP_SUBNET }}" - if (${{ inputs.ENABLE_APP_IDENTITY }}) { - echo "Enable Managed Identity for backend web app" + if ("${{ inputs.ENABLE_APP_IDENTITY }}" -eq "true") { + echo "Enable Managed Identity for ${{ inputs.WEBAPP_NAME }} web app" az webapp identity assign ` --name ${{ inputs.WEBAPP_NAME }} ` --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} From 5af3022902c878dbbe76f02e70d2231a31264d9e Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Tue, 26 Aug 2025 23:37:25 +0200 Subject: [PATCH 47/62] fix: update BackendApp managed identity permissions to use secret for resource group --- .github/workflows/cicd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index cb5a3c9..d0793cd 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -248,7 +248,7 @@ jobs: - name: Grant permissions to the BackendApp managed identity run: | - APP_PRINCIPAL_ID=$(az webapp identity show -g "${{ vars.AZURE_RESOURCE_GROUP }}" -n "$BACKEND_WEBAPP_NAME" --query principalId -o tsv) + APP_PRINCIPAL_ID=$(az webapp identity show -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n "$BACKEND_WEBAPP_NAME" --query principalId -o tsv) echo "Granting Key Vault access to Backend APP ($APP_PRINCIPAL_ID)" az role assignment create \ --assignee-object-id "$APP_PRINCIPAL_ID" \ From 5be281234c827a0dc71c9178ba372c42f9a8bf15 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Tue, 26 Aug 2025 23:49:00 +0200 Subject: [PATCH 48/62] fix: update BackendApp managed identity permissions to use dynamic web app name --- .github/workflows/cicd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index d0793cd..a3523da 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -248,7 +248,7 @@ jobs: - name: Grant permissions to the BackendApp managed identity run: | - APP_PRINCIPAL_ID=$(az webapp identity show -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n "$BACKEND_WEBAPP_NAME" --query principalId -o tsv) + APP_PRINCIPAL_ID=$(az webapp identity show -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n "rest-api-${{ vars.WEBAPP_BACKEND_NAME }}" --query principalId -o tsv) echo "Granting Key Vault access to Backend APP ($APP_PRINCIPAL_ID)" az role assignment create \ --assignee-object-id "$APP_PRINCIPAL_ID" \ From cc691f32ce03b3d0486ce39bcc5cea0933b73cd1 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Wed, 27 Aug 2025 00:07:17 +0200 Subject: [PATCH 49/62] fix: update scope for BackendApp managed identity permissions to use variable for subscription ID --- .github/workflows/cicd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index a3523da..ff9cfc3 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -254,7 +254,7 @@ jobs: --assignee-object-id "$APP_PRINCIPAL_ID" \ --assignee-principal-type ServicePrincipal \ --role "Key Vault Secrets User" \ - --scope /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ secrets.AZURE_RESOURCE_GROUP }}/providers/Microsoft.Web/sites/rest-api-${{ vars.WEBAPP_BACKEND_NAME }} + --scope /subscriptions/${{ vars.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ secrets.AZURE_RESOURCE_GROUP }}/providers/Microsoft.Web/sites/rest-api-${{ vars.WEBAPP_BACKEND_NAME }} - name: Create frontend_app uses: ./.github/actions/CreateWebAPP From 6d7d533565d4791c99fe2c2530741fe4236d0498 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Wed, 27 Aug 2025 21:04:55 +0200 Subject: [PATCH 50/62] fix: add managed identity assignment for web app creation --- .github/actions/CreateWebAPP/action.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/actions/CreateWebAPP/action.yaml b/.github/actions/CreateWebAPP/action.yaml index bd8d58d..b5e217d 100644 --- a/.github/actions/CreateWebAPP/action.yaml +++ b/.github/actions/CreateWebAPP/action.yaml @@ -50,15 +50,21 @@ runs: --plan ${{ inputs.AZURE_APP_SERVICE_PLAN_NAME }} ` --container-registry-url ghcr.io ` --container-image-name $CONTAINER_IMAGE_NAME + + if ("${{ inputs.ENABLE_APP_IDENTITY }}" -eq "true") { + echo "Enable Managed Identity for ${{ inputs.WEBAPP_NAME }} web app" + az webapp identity assign ` + --name ${{ inputs.WEBAPP_NAME }} ` + --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} + } if ('${{ inputs.WEBAPP_SETTINGS }}' -match '^(\w+="[^"]+" ?)+$') { - echo "Set environment variables for ${{ inputs.WEBAPP_NAME }}" + echo "Set environment variables for ${{ inputs.WEBAPP_NAME }}" az webapp config appsettings set ` --name ${{ inputs.WEBAPP_NAME }} ` --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} ` --settings ${{ inputs.WEBAPP_SETTINGS }} - } - else { + } else { echo "No environment variables to set." } @@ -75,9 +81,3 @@ runs: --vnet "${{ inputs.WEBAPP_VNET_NAME }}" ` --subnet "${{ inputs.WEBAPP_SUBNET }}" - if ("${{ inputs.ENABLE_APP_IDENTITY }}" -eq "true") { - echo "Enable Managed Identity for ${{ inputs.WEBAPP_NAME }} web app" - az webapp identity assign ` - --name ${{ inputs.WEBAPP_NAME }} ` - --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} - } From 3992501ff5fbb292d88a0bfe3a9ff1050522a970 Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Wed, 27 Aug 2025 21:59:59 +0200 Subject: [PATCH 51/62] fix: add inputs for VNet and Subnet names and implement NSG linking to Subnet --- .github/actions/CreateNSGRule/action.yaml | 23 +++++++++++++++++++++++ .github/workflows/cicd.yaml | 7 ++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/.github/actions/CreateNSGRule/action.yaml b/.github/actions/CreateNSGRule/action.yaml index e6c3618..64f0c47 100644 --- a/.github/actions/CreateNSGRule/action.yaml +++ b/.github/actions/CreateNSGRule/action.yaml @@ -13,6 +13,14 @@ inputs: description: "Azure Location" required: true type: string + AZURE_VNET_NAME: + description: "Azure VNet Name" + required: true + type: string + AZURE_SUBNET_NAME: + description: "Azure Subnet Name" + required: true + type: string NSG_RULE_NAME: description: "Azure NSG Rule Name" required: false @@ -64,3 +72,18 @@ runs: fi PRIO=$((PRIO + 100)) done + + - name: Assign NSG to Subnet + shell: bash + run: | + echo "Checking if '${{ inputs.AZURE_NSG_NAME }}' NSG is linked to subnet..." + SUBNET_ID=$(az network vnet subnet show --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} --vnet-name ${{ inputs.AZURE_VNET_NAME }} --name ${{ inputs.AZURE_SUBNET_NAME }} --query id --output tsv) + if az network vnet subnet show --ids $SUBNET_ID --query networkSecurityGroup.id --output tsv | grep -q ${{ inputs.AZURE_NSG_NAME }}; then + echo "NSG is already linked to the subnet." + else + echo "Linking NSG to subnet..." + az network vnet subnet update \ + --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} \ + --ids $SUBNET_ID \ + --network-security-group ${{ inputs.AZURE_NSG_NAME }} + fi diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index ff9cfc3..06df785 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -80,7 +80,8 @@ jobs: AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} NSG_RULE_NAME: "AllowTraffic4Port" ALLOWED_PORTS: "80,443" - + AZURE_VNET_NAME: ${{ vars.AZURE_VNET_NAME }} + AZURE_SUBNET_NAME: ${{ vars.AZURE_SUBNET_FE }} # No need any specific rules for BE NSG # Needed functionality covered by default rules - name: Create BE NSG @@ -89,6 +90,8 @@ jobs: AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_BE }} AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} + AZURE_VNET_NAME: ${{ vars.AZURE_VNET_NAME }} + AZURE_SUBNET_NAME: ${{ vars.AZURE_SUBNET_FE }} # No need any specific rules for BE NSG # Needed functionality covered by default rules @@ -98,6 +101,8 @@ jobs: AZURE_RESOURCE_GROUP: ${{ secrets.AZURE_RESOURCE_GROUP }} AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_DB }} AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} + AZURE_VNET_NAME: ${{ vars.AZURE_VNET_NAME }} + AZURE_SUBNET_NAME: ${{ vars.AZURE_SUBNET_FE }} - name: Create Key Vault run: | From a16691081397642c500241c18559f5d2f4cb4174 Mon Sep 17 00:00:00 2001 From: Yurii Berezan <67425022+YuriiBerezan@users.noreply.github.com> Date: Wed, 27 Aug 2025 22:12:40 +0200 Subject: [PATCH 52/62] fix: update subnet names for backend and database NSGs in CI/CD workflow --- .github/workflows/cicd.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 06df785..dff3fd6 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -91,7 +91,7 @@ jobs: AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_BE }} AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} AZURE_VNET_NAME: ${{ vars.AZURE_VNET_NAME }} - AZURE_SUBNET_NAME: ${{ vars.AZURE_SUBNET_FE }} + AZURE_SUBNET_NAME: ${{ vars.AZURE_SUBNET_BE }} # No need any specific rules for BE NSG # Needed functionality covered by default rules @@ -102,7 +102,7 @@ jobs: AZURE_NSG_NAME: ${{ vars.AZURE_NSG_NAME_DB }} AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} AZURE_VNET_NAME: ${{ vars.AZURE_VNET_NAME }} - AZURE_SUBNET_NAME: ${{ vars.AZURE_SUBNET_FE }} + AZURE_SUBNET_NAME: ${{ vars.AZURE_SUBNET_DB }} - name: Create Key Vault run: | From b61fe2f13d2d3da59c4e90e99f9644daf1b07544 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Thu, 28 Aug 2025 21:54:03 +0200 Subject: [PATCH 53/62] fix: update VNet creation step and add private endpoint for Key Vault --- .github/actions/CreateWebAPP/action.yaml | 1 - .github/workflows/cicd.yaml | 33 +++++++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/.github/actions/CreateWebAPP/action.yaml b/.github/actions/CreateWebAPP/action.yaml index b5e217d..3d496e6 100644 --- a/.github/actions/CreateWebAPP/action.yaml +++ b/.github/actions/CreateWebAPP/action.yaml @@ -80,4 +80,3 @@ runs: --resource-group ${{ inputs.AZURE_RESOURCE_GROUP }} ` --vnet "${{ inputs.WEBAPP_VNET_NAME }}" ` --subnet "${{ inputs.WEBAPP_SUBNET }}" - diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index dff3fd6..d1fa6c1 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -30,7 +30,7 @@ jobs: tenant-id: ${{ vars.AZURE_TENANT_ID }} subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} - - name: Create VNet + - name: Create VNet and Subnets run: | echo "Checking if VNet exists..." if az network vnet show --name ${{ vars.AZURE_VNET_NAME }} --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }}; then @@ -43,7 +43,7 @@ jobs: --location ${{ vars.AZURE_LOCATION }} \ --address-prefixes 10.0.0.0/16 fi - address_prefix=1 + address_prefix=0 # Define subnet delegation mapping declare -A subnet_delegations @@ -51,7 +51,7 @@ jobs: subnet_delegations["${{ vars.AZURE_SUBNET_FE }}"]="Microsoft.Web/serverFarms" subnet_delegations["${{ vars.AZURE_SUBNET_DB }}"]="Microsoft.DBforPostgreSQL/flexibleServers" - for subnet in ${{ vars.AZURE_SUBNET_BE }} ${{ vars.AZURE_SUBNET_FE }} ${{ vars.AZURE_SUBNET_DB }} ; do + for subnet in Default ${{ vars.AZURE_SUBNET_BE }} ${{ vars.AZURE_SUBNET_FE }} ${{ vars.AZURE_SUBNET_DB }} ; do echo "Creating Subnet for $subnet..." delegation="${subnet_delegations[$subnet]}" if [[ -n "$delegation" ]]; then @@ -127,6 +127,33 @@ jobs: --role "Key Vault Secrets Officer" \ --scope "/subscriptions/${{ vars.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ secrets.AZURE_RESOURCE_GROUP }}/providers/Microsoft.KeyVault/vaults/${{ vars.AZURE_KEY_VAULT_NAME }}" + - name: Create Key Vault Private Endpoint + run: | + SUBNET="Default" + PE=restapi-kv-pe-we-01 + ZONE=privatelink.vaultcore.azure.net + CONN_NAME=restapi-kv-pe-conn + + # get resource IDs + KV_ID=$(az keyvault show -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n ${{ vars.AZURE_KEY_VAULT_NAME }} --query id -o tsv) + VNET_ID=$(az network vnet show -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n ${{ vars.AZURE_VNET_NAME }} --query id -o tsv) + + echo "Creating private DNS zone for Key Vault" + az network private-dns zone create -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n $ZONE + az network private-dns link vnet create -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n kv-dns-link \ + --zone-name $ZONE --virtual-network $VNET_ID --registration-enabled false + + echo "Creating private endpoint" + az network private-endpoint create -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n $PE \ + --vnet-name ${{ vars.AZURE_VNET_NAME }} --subnet $SUBNET \ + --private-connection-resource-id $KV_ID \ + --group-ids vault --connection-name $CONN_NAME + + echo "Attaching the PE to the DNS zone (auto creates A record)" + PE_ID=$(az network private-endpoint show -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n $PE --query id -o tsv) + az network private-endpoint dns-zone-group create -g "${{ secrets.AZURE_RESOURCE_GROUP }}" --endpoint-name $PE \ + -n kv-dns-group --private-dns-zone $ZONE --zone-name $ZONE + - name: Create PostgreSQL Server id: create_postgres_server run: | From c982a14ae185efc515866e3b23384fc7d9718949 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Thu, 28 Aug 2025 22:03:57 +0200 Subject: [PATCH 54/62] fix: update CI/CD workflow for improved resource deployment --- .github/workflows/cicd.yaml | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index d1fa6c1..19e6d44 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -117,15 +117,20 @@ jobs: --location ${{ vars.AZURE_LOCATION }} fi - az role assignment create \ - --assignee ${{ vars.DEV_GROUP_ID }} \ - --role "Key Vault Secrets Officer" \ - --scope "/subscriptions/${{ vars.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ secrets.AZURE_RESOURCE_GROUP }}/providers/Microsoft.KeyVault/vaults/${{ vars.AZURE_KEY_VAULT_NAME }}" + KEY_VAULT_ID=$(az keyvault show --name ${{ vars.AZURE_KEY_VAULT_NAME }} --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} --query id -o tsv) - az role assignment create \ - --assignee ${{ vars.AZURE_CLIENT_ID }} \ - --role "Key Vault Secrets Officer" \ - --scope "/subscriptions/${{ vars.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ secrets.AZURE_RESOURCE_GROUP }}/providers/Microsoft.KeyVault/vaults/${{ vars.AZURE_KEY_VAULT_NAME }}" + for assignee in ${{ vars.DEV_GROUP_ID }} ${{ vars.AZURE_CLIENT_ID }} + do + existing=$(az role assignment list --scope "$KEY_VAULT_ID" --assignee "$assignee" --role "$roleDefinitionId" --query "[].id" -o tsv) + if [ -z "$existing" ]; then + az role assignment create \ + --assignee "$assignee" \ + --role "Key Vault Secrets Officer" \ + --scope $KEY_VAULT_ID + else + echo "Role assignment already exists, skipping creation." + fi + done - name: Create Key Vault Private Endpoint run: | @@ -189,9 +194,7 @@ jobs: --server-name "${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}" \ --database-name tasks_db fi - echo "::add-mask::$PASSWORD" - echo "AZURE-POSTGRESQL-ADMIN-PASSWORD=$PASSWORD" >> $GITHUB_ENV build_docker_image: name: Build docker image and save in Github Container Registry @@ -220,9 +223,9 @@ jobs: source_path: backend_app tag: latest docker_args: | - POSTGRES_PASSWORD="11qewewew" - POSTGRES_HOST="${{ vars.AZURE_POSTGRESQL_SERVER_NAME }}.postgres.database.azure.com" - POSTGRES_USER="${{ secrets.AZURE_POSTGRESQL_ADMIN_USER }}" + POSTGRES_PASSWORD="Placeholder" + POSTGRES_HOST="Placeholder" + POSTGRES_USER="Placeholder" - name: Build and push FE Docker image uses: ./.github/actions/CreateDockerImage From bd73018ee263ffa5613f75ecceaf13b7e03db5e4 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Thu, 28 Aug 2025 22:09:03 +0200 Subject: [PATCH 55/62] fix: update CI/CD workflow for improved resource deployment --- .github/workflows/cicd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 19e6d44..46d9902 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -121,7 +121,7 @@ jobs: for assignee in ${{ vars.DEV_GROUP_ID }} ${{ vars.AZURE_CLIENT_ID }} do - existing=$(az role assignment list --scope "$KEY_VAULT_ID" --assignee "$assignee" --role "$roleDefinitionId" --query "[].id" -o tsv) + existing=$(az role assignment list --scope "$KEY_VAULT_ID" --assignee "$assignee" --role "Key Vault Secrets Officer" --query "[].id" -o tsv) if [ -z "$existing" ]; then az role assignment create \ --assignee "$assignee" \ From 714e0b65b0967e1b184cd20e82f9f4943c492bf4 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Thu, 28 Aug 2025 22:22:35 +0200 Subject: [PATCH 56/62] fix: update Key Vault private endpoint creation script for improved readability and debugging --- .github/workflows/cicd.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 46d9902..a37f6a3 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -135,12 +135,13 @@ jobs: - name: Create Key Vault Private Endpoint run: | SUBNET="Default" - PE=restapi-kv-pe-we-01 - ZONE=privatelink.vaultcore.azure.net - CONN_NAME=restapi-kv-pe-conn + PE="restapi-kv-pe-we-01" + ZONE="privatelink.vaultcore.azure.net" + CONN_NAME="restapi-kv-pe-conn" # get resource IDs KV_ID=$(az keyvault show -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n ${{ vars.AZURE_KEY_VAULT_NAME }} --query id -o tsv) + echo "[DEBUG] KV_ID: '$KV_ID'" VNET_ID=$(az network vnet show -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n ${{ vars.AZURE_VNET_NAME }} --query id -o tsv) echo "Creating private DNS zone for Key Vault" From 63137961a2470a4c8e653ee08419cf2c9955c2de Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Fri, 29 Aug 2025 00:48:17 +0200 Subject: [PATCH 57/62] fix: add check for existing private DNS zone before creation for Key Vault --- .github/workflows/cicd.yaml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index a37f6a3..edaebfe 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -144,10 +144,21 @@ jobs: echo "[DEBUG] KV_ID: '$KV_ID'" VNET_ID=$(az network vnet show -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n ${{ vars.AZURE_VNET_NAME }} --query id -o tsv) - echo "Creating private DNS zone for Key Vault" - az network private-dns zone create -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n $ZONE - az network private-dns link vnet create -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n kv-dns-link \ - --zone-name $ZONE --virtual-network $VNET_ID --registration-enabled false + # Check if the DNS zone exists + existing_zone=$(az network private-dns zone list --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} --query "[?name=='$ZONE']" -o tsv) + + if [ -z "$existing_zone" ]; then + echo "Creating private DNS zone for Key Vault" + az network private-dns zone create -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n $ZONE + az network private-dns link vnet create \ + -g "${{ secrets.AZURE_RESOURCE_GROUP }}" \ + -n kv-dns-link \ + --zone-name $ZONE \ + --virtual-network $VNET_ID \ + --registration-enabled false + else + echo "DNS zone $ZONE already exists, skipping creation." + fi echo "Creating private endpoint" az network private-endpoint create -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n $PE \ From af6c9f00f2f455bb98e1bc9ca64f5e50c845e48e Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Fri, 29 Aug 2025 00:58:48 +0200 Subject: [PATCH 58/62] DEBUG --- .github/workflows/cicd.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index edaebfe..1984e2a 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -161,6 +161,11 @@ jobs: fi echo "Creating private endpoint" + + echo "[DEBUG] az network private-endpoint create -g '${{ secrets.AZURE_RESOURCE_GROUP }}' -n $PE" + echo "[DEBUG] --vnet-name ${{ vars.AZURE_VNET_NAME }} --subnet $SUBNET" + echo "[DEBUG] --group-ids vault --connection-name $CONN_NAME" + az network private-endpoint create -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n $PE \ --vnet-name ${{ vars.AZURE_VNET_NAME }} --subnet $SUBNET \ --private-connection-resource-id $KV_ID \ From 4d426eac69ee56c6a937a2184a277ba94ed9256c Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Fri, 29 Aug 2025 01:04:41 +0200 Subject: [PATCH 59/62] debug --- .github/workflows/cicd.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 1984e2a..d01d893 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -164,6 +164,7 @@ jobs: echo "[DEBUG] az network private-endpoint create -g '${{ secrets.AZURE_RESOURCE_GROUP }}' -n $PE" echo "[DEBUG] --vnet-name ${{ vars.AZURE_VNET_NAME }} --subnet $SUBNET" + echo "[DEBUG] --private-connection-resource-id $KV_ID" echo "[DEBUG] --group-ids vault --connection-name $CONN_NAME" az network private-endpoint create -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n $PE \ From c74751f19357c08c46cf2f4ccc6a724a70f8bfd7 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Fri, 29 Aug 2025 01:25:41 +0200 Subject: [PATCH 60/62] location added --- .github/workflows/cicd.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index d01d893..0398b6f 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -153,6 +153,7 @@ jobs: az network private-dns link vnet create \ -g "${{ secrets.AZURE_RESOURCE_GROUP }}" \ -n kv-dns-link \ + --location ${{ vars.AZURE_LOCATION }} \ --zone-name $ZONE \ --virtual-network $VNET_ID \ --registration-enabled false From 9db3086c1320527f1bad62069cb7219cff44b150 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Fri, 29 Aug 2025 01:28:34 +0200 Subject: [PATCH 61/62] more places with location --- .github/workflows/cicd.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 0398b6f..25e34ad 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -168,7 +168,9 @@ jobs: echo "[DEBUG] --private-connection-resource-id $KV_ID" echo "[DEBUG] --group-ids vault --connection-name $CONN_NAME" - az network private-endpoint create -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n $PE \ + az network private-endpoint create \ + -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n $PE \ + --location ${{ vars.AZURE_LOCATION }} \ --vnet-name ${{ vars.AZURE_VNET_NAME }} --subnet $SUBNET \ --private-connection-resource-id $KV_ID \ --group-ids vault --connection-name $CONN_NAME From 8465b38c281b9d1e7188494e2ee340862e9069a9 Mon Sep 17 00:00:00 2001 From: Viktor Dronov <107995419+ViktorDronov@users.noreply.github.com> Date: Fri, 29 Aug 2025 02:10:07 +0200 Subject: [PATCH 62/62] fix: streamline Key Vault role assignment and DNS zone creation in CI/CD workflow --- .github/workflows/cicd.yaml | 54 ++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 25e34ad..64785da 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -119,8 +119,7 @@ jobs: KEY_VAULT_ID=$(az keyvault show --name ${{ vars.AZURE_KEY_VAULT_NAME }} --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} --query id -o tsv) - for assignee in ${{ vars.DEV_GROUP_ID }} ${{ vars.AZURE_CLIENT_ID }} - do + for assignee in ${{ vars.DEV_GROUP_ID }} ${{ vars.AZURE_CLIENT_ID }}; do existing=$(az role assignment list --scope "$KEY_VAULT_ID" --assignee "$assignee" --role "Key Vault Secrets Officer" --query "[].id" -o tsv) if [ -z "$existing" ]; then az role assignment create \ @@ -140,8 +139,7 @@ jobs: CONN_NAME="restapi-kv-pe-conn" # get resource IDs - KV_ID=$(az keyvault show -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n ${{ vars.AZURE_KEY_VAULT_NAME }} --query id -o tsv) - echo "[DEBUG] KV_ID: '$KV_ID'" + KEY_VAULT_ID=$(az keyvault show --name ${{ vars.AZURE_KEY_VAULT_NAME }} --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} --query id -o tsv) VNET_ID=$(az network vnet show -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n ${{ vars.AZURE_VNET_NAME }} --query id -o tsv) # Check if the DNS zone exists @@ -149,10 +147,10 @@ jobs: if [ -z "$existing_zone" ]; then echo "Creating private DNS zone for Key Vault" - az network private-dns zone create -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n $ZONE + az network private-dns zone create --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" --name $ZONE az network private-dns link vnet create \ - -g "${{ secrets.AZURE_RESOURCE_GROUP }}" \ - -n kv-dns-link \ + --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ + --name kv-dns-link \ --location ${{ vars.AZURE_LOCATION }} \ --zone-name $ZONE \ --virtual-network $VNET_ID \ @@ -162,23 +160,22 @@ jobs: fi echo "Creating private endpoint" - - echo "[DEBUG] az network private-endpoint create -g '${{ secrets.AZURE_RESOURCE_GROUP }}' -n $PE" - echo "[DEBUG] --vnet-name ${{ vars.AZURE_VNET_NAME }} --subnet $SUBNET" - echo "[DEBUG] --private-connection-resource-id $KV_ID" - echo "[DEBUG] --group-ids vault --connection-name $CONN_NAME" - az network private-endpoint create \ - -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n $PE \ + --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ + --name $PE \ --location ${{ vars.AZURE_LOCATION }} \ --vnet-name ${{ vars.AZURE_VNET_NAME }} --subnet $SUBNET \ - --private-connection-resource-id $KV_ID \ + --private-connection-resource-id $KEY_VAULT_ID \ --group-ids vault --connection-name $CONN_NAME echo "Attaching the PE to the DNS zone (auto creates A record)" - PE_ID=$(az network private-endpoint show -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n $PE --query id -o tsv) - az network private-endpoint dns-zone-group create -g "${{ secrets.AZURE_RESOURCE_GROUP }}" --endpoint-name $PE \ - -n kv-dns-group --private-dns-zone $ZONE --zone-name $ZONE + PE_ID=$(az network private-endpoint show --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" --name $PE --query id -o tsv) + az network private-endpoint dns-zone-group create \ + --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ + --endpoint-name $PE \ + --name kv-dns-group \ + --private-dns-zone $ZONE \ + --zone-name $ZONE - name: Create PostgreSQL Server id: create_postgres_server @@ -304,13 +301,20 @@ jobs: - name: Grant permissions to the BackendApp managed identity run: | - APP_PRINCIPAL_ID=$(az webapp identity show -g "${{ secrets.AZURE_RESOURCE_GROUP }}" -n "rest-api-${{ vars.WEBAPP_BACKEND_NAME }}" --query principalId -o tsv) - echo "Granting Key Vault access to Backend APP ($APP_PRINCIPAL_ID)" - az role assignment create \ - --assignee-object-id "$APP_PRINCIPAL_ID" \ - --assignee-principal-type ServicePrincipal \ - --role "Key Vault Secrets User" \ - --scope /subscriptions/${{ vars.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ secrets.AZURE_RESOURCE_GROUP }}/providers/Microsoft.Web/sites/rest-api-${{ vars.WEBAPP_BACKEND_NAME }} + KEY_VAULT_ID=$(az keyvault show --name ${{ vars.AZURE_KEY_VAULT_NAME }} --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} --query id -o tsv) + APP_PRINCIPAL_ID=$(az webapp identity show -g "${{ secrets.AZURE_RESOURCE_GROUP }}" --name "rest-api-${{ vars.WEBAPP_BACKEND_NAME }}" --query principalId -o tsv) + + existing=$(az role assignment list --scope "$KEY_VAULT_ID" --assignee "$APP_PRINCIPAL_ID" --role "Key Vault Secrets User" --query "[].id" -o tsv) + if [ -z "$existing" ]; then + echo "Granting Key Vault access to Backend APP ($APP_PRINCIPAL_ID)" + az role assignment create \ + --assignee-object-id "$APP_PRINCIPAL_ID" \ + --assignee-principal-type ServicePrincipal \ + --role "Key Vault Secrets User" \ + --scope "$KEY_VAULT_ID" + else + echo "Backend APP ($APP_PRINCIPAL_ID) already has Key Vault access." + fi - name: Create frontend_app uses: ./.github/actions/CreateWebAPP