diff --git a/.github/workflows/azure-pipeline-build.yml b/.github/workflows/azure-pipeline-build.yml deleted file mode 100644 index 0d7719a..0000000 --- a/.github/workflows/azure-pipeline-build.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Build and Release for Azure Pipeline -on: - push: - branches: - - main - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Set up Node.js - uses: actions/setup-node@v2 - with: - node-version: '14' - - - name: Install dependencies and build - run: | - cd ./azure-pipeline/src - npm install - - - name: Install tfx-cli - run: | - npm install -g tfx-cli - - - name: Create extension - run: | - tfx extension create --manifest-globs ./azure-pipeline/vss-extension.json - - - name: Upload VSIX file - uses: actions/upload-artifact@v2 - with: - name: VSIX file - path: ./azure-pipeline/*.vsix diff --git a/README.md b/README.md index c4e1a79..6076693 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,200 @@ -# NGINXaaS for Azure Configuration Automation +# NGINXaaS for Azure Deployment Action -This repository hosts two essential projects that facilitate the management and deployment of NGINXaaS for Azure configurations. Both projects aim to automate the process of synchronizing NGINX configuration files, but they target different platforms and have unique features and notation. To learn more about NGINXaaS for Azure, please review the [product homepage](https://www.nginx.com/products/nginx/nginxaas-for-azure/). +This action supports managing the configuration of an [NGINXaaS for Azure](https://docs.nginx.com/nginxaas/azure/quickstart/overview/) deployment in a GitHub repository. It enables continuous deployment through GitHub workflows to automatically update the NGINXaaS for Azure deployment when changes are made to the NGINX configuration files stored in the repository. Additionally, one can update NGINX certificates that are already present in Azure key vault. -## Projects +## Connecting to Azure -### 1. Azure Pipeline Task +This action leverages the [Azure Login](https://github.com/marketplace/actions/azure-login) action for authenticating with Azure and performing update to an NGINXaaS for Azure deployment. Two different ways of authentication are supported: +- [Service principal with secrets](https://docs.microsoft.com/en-us/azure/developer/github/connect-from-azure?tabs=azure-portal%2Cwindows#use-the-azure-login-action-with-a-service-principal-secret) +- [OpenID Connect](https://docs.microsoft.com/en-us/azure/developer/github/connect-from-azure?tabs=azure-portal%2Cwindows#use-the-azure-login-action-with-openid-connect) (OIDC) with an Azure service principal using a Federated Identity Credential -This project provides a custom Azure pipeline task that automates the synchronization of NGINXaaS configuration files for Azure deployments. It supports both GitHub Action pipelines and Azure DevOps pipeline tasks. [Read more](./azure-pipeline/README.md). +### Sample workflow that authenticates with Azure using an Azure service principal with a secret -### 2. GitHub Action Task +```yaml +# File: .github/workflows/nginxForAzureDeploy.yml -This GitHub Action enables continuous deployment through GitHub workflows to automatically update the NGINXaaS for Azure deployment when changes are made to the NGINX configuration files stored in the repository. It also supports updating NGINX certificates that are present in Azure key vault. [Read more](./github-action/README.md). +name: Sync the NGINX configuration from the GitHub repository to an NGINXaaS for Azure deployment +on: + push: + branches: + - main + paths: + - config/** -## Comparison +jobs: + Deploy-NGINX-Configuration: + runs-on: ubuntu-latest + steps: + - name: 'Checkout repository' + uses: actions/checkout@v2 -| Feature/Aspect | Azure Pipeline Task | GitHub Action Task | -|------------------------------------|------------------------------------|-------------------------------------| -| **CI/CD Automation** | Yes | Yes | -| **Configuration File Host** | GitHub Repos or Azure Repos | GitHub | -| **Security** | Pipelines in secured agents | Azure Login Action | -| **Authentication Methods** | Service Connection | Service Principal with Secrets, OIDC| -| **Certificate Handling** | No | Yes (Azure key vault) | -| **CI/CD Platform** | Azure DevOps | GitHub | + - name: 'Run Azure Login using an Azure service principal with a secret' + uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} -## Getting Started + - name: 'Sync the NGINX configuration from the GitHub repository to the NGINXaaS for Azure deployment' + uses: nginxinc/nginx-for-azure-deploy-action/github-action@v0.4.0 + with: + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + resource-group-name: ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} + nginx-deployment-name: ${{ secrets.NGINX_DEPLOYMENT_NAME }} + nginx-config-directory-path: config/ + nginx-root-config-file: nginx.conf + transformed-nginx-config-directory-path: /etc/nginx/ + debug: false +``` -To get started with either of these projects, please refer to the detailed README files linked above. +### Sample workflow that authenticates with Azure using OIDC -## License +```yaml +# File: .github/workflows/nginxForAzureDeploy.yml -These projects are licensed under the Apache-2.0 License - see the [LICENSE.md](LICENSE) file for details. \ No newline at end of file +name: Sync the NGINX configuration from the GitHub repository to an NGINXaaS for Azure deployment +on: + push: + branches: + - main + paths: + - config/** + +permissions: + id-token: write + contents: read + +jobs: + Deploy-NGINX-Configuration: + runs-on: ubuntu-latest + steps: + - name: 'Checkout repository' + uses: actions/checkout@v2 + + - name: 'Run Azure Login using OIDC' + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: 'Sync the NGINX configuration from the GitHub repository to the NGINXaaS for Azure deployment' + uses: nginxinc/nginx-for-azure-deploy-action/github-action@v0.4.0 + with: + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + resource-group-name: ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} + nginx-deployment-name: ${{ secrets.NGINX_DEPLOYMENT_NAME }} + nginx-config-directory-path: config/ + nginx-root-config-file: nginx.conf + transformed-nginx-config-directory-path: /etc/nginx/ + debug: false +``` + +> **Note:** +The service principal being used for authenticating with Azure should have access to manage the NGINXaaS deployment. For simplicity, this guide assumes that the service principal has `Contributor` role to manage the deployment. Refer [prerequisites](https://docs.nginx.com/nginxaas/azure/getting-started/prerequisites/) for details. + +## Handling NGINX configuration file paths + +To facilitate the migration of the existing NGINX configuration, NGINXaaS for Azure supports multiple-files configuration with each file uniquely identified by a file path, just like how NGINX configuration files are created and used in a self-hosting machine. An NGINX configuration file can include another file using the [include directive](https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/). The file path used in an `include` directive can either be an absolute path or a relative path to the [prefix path](https://www.nginx.com/resources/wiki/start/topics/tutorials/installoptions/). + +The following example shows two NGINX configuration files inside the `/etc/nginx` directory on disk are copied and stored in a GitHub repository under its `config` directory. + +| File path on disk | File path in the repository | +| ------------------------------------ | --------------------------------- | +| /etc/nginx/nginx.conf | /config/nginx.conf | +| /etc/nginx/sites-enabled/mysite.conf | /config/sites-enabled/mysite.conf | + +To use this action to sync the configuration files from this example, the directory path relative to the GitHub repository root `config/` is set to the action's input `nginx-config-directory-path` for the action to find and package the configuration files. The root file `nginx.conf` is set to the input `nginx-root-config-file`. + +```yaml + - name: 'Sync the NGINX configuration from the GitHub repository to the NGINXaaS for Azure deployment' + uses: nginxinc/nginx-for-azure-deploy-action/github-action@v0.4.0 + with: + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + resource-group-name: ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} + nginx-deployment-name: ${{ secrets.NGINX_DEPLOYMENT_NAME }} + nginx-config-directory-path: config/ + nginx-root-config-file: nginx.conf + debug: false +``` + +By default, the action uses a file's relative path to `nginx-config-directory-path` in the repository as the file path in the NGINXaaS for Azure deployment. + +| File path on disk | File path in the repository | File path in the NGINXaaS for Azure deployment | +| ------------------------------------ | --------------------------------- | ---------------------------------------------- | +| /etc/nginx/nginx.conf | /config/nginx.conf | nginx.conf | +| /etc/nginx/sites-enabled/mysite.conf | /config/sites-enabled/mysite.conf | sites-enabled/mysite.conf | + +The default file path handling works for the case of using relative paths in `include` directives, for example, if the root `nginx.conf` references `mysite.conf` using: + +``` +include sites-enabled/mysite.conf; +``` + +For the case of using absolute paths in `include` directives, for example, if the root `nginx.conf` references `mysite.conf` using: + +``` +include /etc/nginx/sites-enabled/mysite.conf; +``` + +The action supports an optional input `transformed-nginx-config-directory-path` to transform the absolute path of the configuration directory in the NGINXaaS for Azure deployment. The absolute configuration directory path on disk `/etc/nginx/` can be set to `transformed-nginx-config-directory-path` as follows to ensure the configuration files using absolute paths in `include` directives work as expected in the NGINXaaS for Azure deployment. + +```yaml + - name: 'Sync the NGINX configuration from the Git repository to the NGINXaaS for Azure deployment' + uses: nginxinc/nginx-for-azure-deploy-action/github-action@v0.4.0 + with: + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + resource-group-name: ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} + nginx-deployment-name: ${{ secrets.NGINX_DEPLOYMENT_NAME }} + nginx-config-directory-path: config/ + nginx-root-config-file: nginx.conf + transformed-nginx-config-directory-path: /etc/nginx/ + debug: false +``` +The transformed paths of the two configuration files in the NGINXaaS for Azure deployment are summarized in the following table + +| File path on disk | File path in the repository | File path in the NGINXaaS for Azure deployment | +| ------------------------------------ | --------------------------------- | ---------------------------------------------- | +| /etc/nginx/nginx.conf | /config/nginx.conf | /etc/nginx/nginx.conf | +| /etc/nginx/sites-enabled/mysite.conf | /config/sites-enabled/mysite.conf | /etc/nginx/sites-enabled/mysite.conf | + +## Handling NGINX certificates + +Since certificates are secrets, it is assumed they are stored in Azure key vault. One can provide multiple certificate entries to the github action as an array of JSON objects with keys: + +`certificateName`- A unique name for the certificate entry + +`keyvaultSecret`- The secret ID for the certificate on Azure key vault + +`certificateVirtualPath`- This path must match one or more ssl_certificate directive file arguments in your Nginx configuration; and must be unique between certificates within the same deployment + +`keyVirtualPath`- This path must match one or more ssl_certificate_key directive file arguments in your Nginx configuration; and must be unique between certificates within the same deployment + +See the example below + +```yaml +- name: "Sync NGINX certificates to NGINXaaS for Azure" + uses: nginxinc/nginx-for-azure-deploy-action/github-action@v0.4.0 + with: + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + resource-group-name: ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} + nginx-deployment-name: ${{ secrets.NGINX_DEPLOYMENT_NAME }} + nginx-deployment-location: ${{ secrets.NGINX_DEPLOYMENT_LOCATION }} + nginx-certificates: '[{"certificateName": "$NGINX_CERT_NAME", "keyvaultSecret": "https://$NGINX_VAULT_NAME.vault.azure.net/secrets/$NGINX_CERT_NAME", "certificateVirtualPath": "/etc/nginx/ssl/my-cert.crt", "keyVirtualPath": "/etc/nginx/ssl/my-cert.key" } ]' + debug: false +``` + +## Handling NGINX configuration and certificates + +```yaml + - name: "Sync NGINX configuration- multi file and certificate to NGINXaaS for Azure" + uses: nginxinc/nginx-for-azure-deploy-action/github-action@v0.4.0 + with: + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + resource-group-name: ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} + nginx-deployment-name: ${{ secrets.NGINX_DEPLOYMENT_NAME }} + nginx-deployment-location: ${{ secrets.NGINX_DEPLOYMENT_LOCATION }} + nginx-config-directory-path: config/ + nginx-root-config-file: nginx.conf + transformed-nginx-config-directory-path: /etc/nginx/ + nginx-certificates: '[{"certificateName": "$NGINX_CERT_NAME", "keyvaultSecret": "https://$NGINX_VAULT_NAME.vault.azure.net/secrets/$NGINX_CERT_NAME", "certificateVirtualPath": "/etc/nginx/ssl/my-cert.crt", "keyVirtualPath": "/etc/nginx/ssl/my-cert.key" } ]' + debug: false +``` diff --git a/github-action/action.yml b/action.yml similarity index 65% rename from github-action/action.yml rename to action.yml index 3d2aa6c..5e8f3e6 100644 --- a/github-action/action.yml +++ b/action.yml @@ -24,14 +24,19 @@ inputs: default: "nginx.conf" transformed-nginx-config-directory-path: description: > - 'The transformed absolute path of the NGINX configuration directory in NGINXaaS for Azure deployment, example: "/etc/nginx/". - If the "include" directive in the NGINX configuration files uses absolute paths, the path transformation - can be used to overwrite the file paths when the action synchronizes the files to the NGINXaaS for Azure deployment.' + 'The absolute directory path in the NGINXaaS for Azure deployment where your configuration files will be placed. + All files found in the nginx-config-directory-path will be copied to this location in the deployment. + For example, use "/etc/nginx/" to match the standard NGINX directory structure on Azure. + If your NGINX configuration files use absolute paths in "include" directives, this setting ensures those paths are correctly mapped in the deployment by prepending the specified directory.' required: false default: "" nginx-certificates: description: 'An array of JSON objects each with keys nginx_cert_name, keyvault_secret, certificate_virtual_path and key_virtual_path. Example: [{"certificateName": "server1", "keyvaultSecret": "https://...", "certificateVirtualPath": "/etc/ssl/certs/server1.crt", "keyVirtualPath": "/etc/ssl/certs/server1.key" }, {"name": "server2", "keyvaultSecret": "https://...", "certificateVirtualPath": "/etc/ssl/certs/server2.crt", "keyVirtualPath": "/etc/ssl/certs/server2.key" }] ' required: false + protected-files: + description: "Comma-separated list of file paths relative to nginx-config-directory-path that should be marked as protected. Example: 'ssl/private.key,conf.d/secrets.conf'" + required: false + default: "" debug: description: "Enable/Disable debug output." required: false @@ -40,10 +45,10 @@ runs: using: "composite" steps: - name: "Synchronize NGINX certificate(s) from the Git repository to an NGINXaaS for Azure deployment" - run: ${{github.action_path}}/src/deploy-certificate.sh --subscription_id=${{ inputs.subscription-id }} --resource_group_name=${{ inputs.resource-group-name }} --nginx_deployment_name=${{ inputs.nginx-deployment-name }} --nginx_resource_location=${{ inputs.nginx-deployment-location }} --certificates=${{ toJSON(inputs.nginx-certificates) }} --debug=${{ inputs.debug }} + run: ${{github.action_path}}/src/deploy-certificate.sh --subscription-id=${{ inputs.subscription-id }} --resource-group-name=${{ inputs.resource-group-name }} --nginx-deployment-name=${{ inputs.nginx-deployment-name }} --nginx-resource-location=${{ inputs.nginx-deployment-location }} --certificates=${{ toJSON(inputs.nginx-certificates) }} --debug=${{ inputs.debug }} if: ${{ inputs.nginx-deployment-location != '' && inputs.nginx-certificates != '' }} shell: bash - name: "Synchronize NGINX configuration from the Git repository to an NGINXaaS for Azure deployment" - run: ${{github.action_path}}/src/deploy-config.sh --subscription_id=${{ inputs.subscription-id }} --resource_group_name=${{ inputs.resource-group-name }} --nginx_deployment_name=${{ inputs.nginx-deployment-name }} --config_dir_path=${{ inputs.nginx-config-directory-path }} --root_config_file=${{ inputs.nginx-root-config-file }} --transformed_config_dir_path=${{ inputs.transformed-nginx-config-directory-path }} --debug=${{ inputs.debug }} + run: ${{github.action_path}}/src/deploy-config.sh --subscription-id=${{ inputs.subscription-id }} --resource-group-name=${{ inputs.resource-group-name }} --nginx-deployment-name=${{ inputs.nginx-deployment-name }} --nginx-config-directory-path=${{ inputs.nginx-config-directory-path }} --nginx-root-config-file=${{ inputs.nginx-root-config-file }} --transformed-nginx-config-directory-path=${{ inputs.transformed-nginx-config-directory-path }} --protected-files=${{ inputs.protected-files }} --debug=${{ inputs.debug }} if: ${{ inputs.nginx-config-directory-path != '' }} shell: bash diff --git a/azure-pipeline/.gitignore b/azure-pipeline/.gitignore deleted file mode 100644 index bbe9467..0000000 --- a/azure-pipeline/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules -dist -package-lock.json -coverage diff --git a/azure-pipeline/example.yml b/azure-pipeline/example.yml deleted file mode 100644 index fb5dfcf..0000000 --- a/azure-pipeline/example.yml +++ /dev/null @@ -1,16 +0,0 @@ -trigger: -- main - -pool: - vmImage: ubuntu-latest - -steps: -- task: nginx-config-push@0 - inputs: - serviceConnectionName: '(Enter the name of the service connection to Azure)' - resourceGroupName: '(Enter the name of the Azure resource group of the deployment)' - subscriptionId: '(Enter the Azure subscription ID of the deployment)' - deploymentName: '(Enter the name for this deployment)' - configDirectoryInRepo: '(Enter the relative path to the Nginx configuration directory in the repository)' - configDirectoryInDeployment: '(Enter the target path for the Nginx configuration directory in the deployment environment, e.g., /etc/nginx)' - rootConfigFileName: '(Enter the name of the root configuration file and make sure it is in the config directory. e.g., nginx.conf)' diff --git a/azure-pipeline/images/extension-icon.png b/azure-pipeline/images/extension-icon.png deleted file mode 100644 index 6d8d2f8..0000000 Binary files a/azure-pipeline/images/extension-icon.png and /dev/null differ diff --git a/azure-pipeline/images/readme-guidline-01.png b/azure-pipeline/images/readme-guidline-01.png deleted file mode 100644 index 4d1a353..0000000 Binary files a/azure-pipeline/images/readme-guidline-01.png and /dev/null differ diff --git a/azure-pipeline/images/readme-guidline-02.png b/azure-pipeline/images/readme-guidline-02.png deleted file mode 100644 index cd5ff5d..0000000 Binary files a/azure-pipeline/images/readme-guidline-02.png and /dev/null differ diff --git a/azure-pipeline/images/readme-guidline-03.png b/azure-pipeline/images/readme-guidline-03.png deleted file mode 100644 index 8be3ac8..0000000 Binary files a/azure-pipeline/images/readme-guidline-03.png and /dev/null differ diff --git a/azure-pipeline/images/readme-guidline-04.png b/azure-pipeline/images/readme-guidline-04.png deleted file mode 100644 index e538037..0000000 Binary files a/azure-pipeline/images/readme-guidline-04.png and /dev/null differ diff --git a/azure-pipeline/images/readme-guidline-05.png b/azure-pipeline/images/readme-guidline-05.png deleted file mode 100644 index 2044774..0000000 Binary files a/azure-pipeline/images/readme-guidline-05.png and /dev/null differ diff --git a/azure-pipeline/images/readme-marketplace.png b/azure-pipeline/images/readme-marketplace.png deleted file mode 100644 index 2efb2ff..0000000 Binary files a/azure-pipeline/images/readme-marketplace.png and /dev/null differ diff --git a/azure-pipeline/images/readme-personal-access-token.png b/azure-pipeline/images/readme-personal-access-token.png deleted file mode 100644 index 1a8bbb2..0000000 Binary files a/azure-pipeline/images/readme-personal-access-token.png and /dev/null differ diff --git a/azure-pipeline/images/readme-service-connections.png b/azure-pipeline/images/readme-service-connections.png deleted file mode 100644 index dc3852d..0000000 Binary files a/azure-pipeline/images/readme-service-connections.png and /dev/null differ diff --git a/azure-pipeline/images/readme-token-scopes.png b/azure-pipeline/images/readme-token-scopes.png deleted file mode 100644 index 6b62ce4..0000000 Binary files a/azure-pipeline/images/readme-token-scopes.png and /dev/null differ diff --git a/azure-pipeline/jest.config.js b/azure-pipeline/jest.config.js deleted file mode 100644 index 560aa1c..0000000 --- a/azure-pipeline/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - roots: ['/src'], - transform: { - '^.+\\.tsx?$': 'ts-jest', - }, - testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$', - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], -}; \ No newline at end of file diff --git a/azure-pipeline/package.json b/azure-pipeline/package.json deleted file mode 100644 index 075d80b..0000000 --- a/azure-pipeline/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "nginx-for-azure-config-sync", - "version": "2.0.0", - "description": "", - "main": "index.js", - "scripts": { - "start": "tsc && node src/dist/index.js", - "test": "jest src/test --verbose --coverage" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "@azure/identity": "^2.1.0", - "@types/tar": "^6.1.1", - "axios": "^0.27.2", - "azure-pipelines-task-lib": "^4.0.0-preview", - "fs-extra": "^10.1.0", - "gunzip-maybe": "^1.4.2", - "tar": "^6.1.11", - "tar-fs": "^2.1.1" - }, - "devDependencies": { - "@types/gunzip-maybe": "^1.4.0", - "@types/jest": "^29.5.3", - "@types/mocha": "^9.1.1", - "@types/node": "^18.0.4", - "@types/q": "^1.5.5", - "jest": "^29.6.2", - "sync-request": "^6.1.0", - "ts-jest": "^29.1.1" - } -} diff --git a/azure-pipeline/readme.md b/azure-pipeline/readme.md deleted file mode 100644 index 4928029..0000000 --- a/azure-pipeline/readme.md +++ /dev/null @@ -1,94 +0,0 @@ -# Azure Pipeline task: NGINX for Azure Configuration Push - -## Overview - -This is a customed Azure pipeline task that automates the process of synchronizing NGINX configuration files. With this pipeline, the configuration files that are stored and managed in Azure DevOps or GitHub repositories can now be automatically packaged and uploaded to NGINX for Azure deployments. - -When creating and updating the configuration of a NGINX for Azure deployment, an automated pipeline task would be very helpful. There are three main advantages of a pipeline task compared to a manual updation: - -##### 1. Automation. - -A pipeline task can be triggered immediately if designated NGINX coniguration files have changes. This would increase effectiveness when there are frequent modifications and updates. - -##### 2. Version control. - -NGINX configuration files could be stored in GitHub or Azure DevOps repositories. In this way, all configure changes would be able to be traced and rolled back if there is a mistake. - -##### 3. Security. -Pipelines are running in secured and reliable agents, where data is transmitted within Azure services, hence reduced unnecessary data distribution and delivery. - -NGINX for Azure currently supports both **GitHub Action** pipeline and **Azure DevOps** pipeline task. While there are some authentication and repository supporting difference, the purpose and functionality of the two pipelines have no essential distinction. For details on GitHub Action pipeline, please check out NGINX for Azure Deployment Action for more details. For Azure DevOps pipeline task, please follow the instructions below. - -### Pipeline Task Set-up Guideline - -Basically, all that the pipeline task needed is a .yml task written as follows, - -```yaml -# example.yml - -trigger: -- main - -pool: - vmImage: ubuntu-latest - -steps: -- task: nginx-config-push@0 - inputs: - serviceConnectionName: '(Enter the name of the service connection to Azure)' - resourceGroupName: '(Enter the name of the Azure resource group of the deployment)' - subscriptionId: '(Enter the Azure subscription ID of the deployment)' - deploymentName: '(Enter the name for this deployment)' - configDirectoryInRepo: '(Enter the relative path to the Nginx configuration directory in the repository)' - configDirectoryInDeployment: '(Enter the target path for the Nginx configuration directory in the deployment environment, e.g., /etc/nginx/)' - rootConfigFileName: '(Enter the name of the root configuration file and make sure it is in the config directory. e.g., nginx.conf)' - -``` - -What is needed right now is to get values of the needed variables with following steps: - - -##### 1. Get your NGINX configuration file ready in the repository. - -Upload your NGINX configuration files onto any folder of your Azure DevOps repository. Then update the ‘configDirectoryInRepo’ to the path of the configuration folder in your repository. - -![Image](images/readme-guidline-01.png) - - -##### 2. Make sure your NGINX for Azure deployment is working properly. - -Deploy a NGINX for Azure resource. See Deploy NGINX for Azure | NGINX for Azure Docs. Once your deployment is ready, you will be able to fill in the “resourceGroupName”, “subscriptionId” and the “deploymentName” variable from the portal overview board. - -![Image](images/readme-guidline-02.png) - - - -##### 3. Create a service connetion and grant permission for the pipeline. - -This part would require a little bit more work to set up. -A service connection gives you access to resources in your Azure subscription from your Azure DevOps project. -1. In Azure DevOps, go to the project that contains your target pipeline. In the lower-left corner, select Project settings. -2. Under Pipelines, select Service connections. In the upper-right corner, select New service connection. -3. In New service connection, select Azure Resource Manager. - -![Image](images/readme-guidline-03.png) - -4. In the Authentication method dialog, select Service principal (automatic) to create a new service principal or select Service principal (manual) to use an existing service principal. -5. Enter your subscription and resource and a name for your service connection. - -Also, you may need to assign a proper role assignment to the service connection so that our pipeline can access NGINX for azure deployments. You can add Contributor as your role assignment to this service connection. - -![Image](images/readme-guidline-04.png) - - -Go back to the Azure DevOps project settings and click into the “service connection” tab. Remember the name displayed in the service connections list. This will be the value of ‘serciveConnectionName’ in the .yml file. - -![Image](images/readme-guidline-05.png) - - -##### 4. Install the pipeline task and fill all the needed variables into the .yml file on Azure DevOps. - -Go to marketplace and search our pipeline task. Once you have the extension installed on your Azure DevOps organization, you will be able to create a pipeline task with the stated .yml file template and use our customized task conveniently. You can also use text editor of the pipeline to utilize a simple UI of this pipeline task. - - -That's it! Once you have all the above settings done and a pipeline created on the Azure DevOps, your NGINX configuration will be automatically updated to your designated NGINX for Azure deployment resource once you have any changes to the files in the repository and the pipeline is triggered. \ No newline at end of file diff --git a/azure-pipeline/src/index.ts b/azure-pipeline/src/index.ts deleted file mode 100644 index 09a29f8..0000000 --- a/azure-pipeline/src/index.ts +++ /dev/null @@ -1,129 +0,0 @@ -import taskLib = require('azure-pipelines-task-lib/task'); -import { AxiosRequestConfig } from "axios"; -import { updateUserConfig } from './utils/api'; -import { - API_VERSION, - SCOPE, - OUTPUT_PATH, - INPUT, -} from './utils/constants'; -import { - FileHandler, -} from './utils/file-handler'; -import { - validatedEnvVar, - validatedAuthParams, -} from './utils/validation'; -import { - ClientSecretCredential, - AuthenticationError, -} from '@azure/identity'; - -/** - * Class ConfigUpdater is responsible for updating the Nginx configuration. - * It provides methods to authenticate, compress, and upload user-specific configurations - * using Azure identity and other utilities. -*/ -class ConfigUpdater { - - private f = new FileHandler; - - /** - * Retrieves the authorization token for later authentication by using Azure identity. - * @returns {Promise} The authorization token. - * @throws {Error} If the authorization parameters validation fails. - * @throws {AuthenticationError} If the access token fetch fails. - */ - private getAuthorizationTokenFromKey = async ( - tenantID: string, - clientID: string, - servicePrincipalKey: string - ) => { - try { - const clientSecretCredential: ClientSecretCredential = new ClientSecretCredential( - tenantID, clientID, servicePrincipalKey - ); - const accessToken = await clientSecretCredential.getToken(SCOPE); - if (!accessToken) { - throw new AuthenticationError(0, { - error: 'Access token fetch failed' - }); - } - return accessToken; - } catch (e) { - throw new Error("Authorization parameters validation failed"); - } - } - - /** - * Compresses the configuration file folder and prepares it for sending to the backend. - * @returns {Promise} An object containing properties related to the compressed file. - */ - private getConvertedFileObject = async () => { - const file = await this.f.compressFile( - validatedEnvVar(INPUT.source), - validatedEnvVar(INPUT.target), - OUTPUT_PATH, - ); - return { - properties: { - rootFile: `${validatedEnvVar(INPUT.target)}/${validatedEnvVar(INPUT.rootFile)}`.replace(/\/\/+/g, '/'), - package: { - data: this.f.convertFileToBase64String(file), - } - } - }; - } - - /** - * Constructs the request configuration, including the bearer token from the user's input. - * @returns {Promise} The request configuration. - */ - private getRequestConfig = async () => { - const token = await this.getAuthorizationTokenFromKey( - validatedAuthParams('tenantid'), - validatedAuthParams('servicePrincipalId'), - validatedAuthParams('servicePrincipalKey'), - ); - const config: AxiosRequestConfig = { headers: { - Authorization: `Bearer ${token.token}`, - }} - return config; - } - - /** - * Constructs the required parameters for the uploading API request. - * @returns {Object} An object containing the required parameters for the request, - * including subscription ID, resource group name, deployment name, and API version. - */ - private getRequestResource = () => { - return { - subscriptionId: validatedEnvVar(INPUT.subscription), - resourceGroupName: validatedEnvVar(INPUT.resource), - deploymentName: validatedEnvVar(INPUT.deployment), - apiVersion: API_VERSION, - } - } - - /** - * Main function to compress the file, retrieve authentication info, and call the API to update the Nginx configuration. - * @throws {Error} If the Nginx configuration uploading fails. - */ - updateNginxConfig = async () => { - console.log('updateNginxConfig... \n\tSubscription: ', validatedEnvVar(INPUT.subscription), '\n\tResource Group: ', validatedEnvVar(INPUT.resource), '\n\tDeployment: ', validatedEnvVar(INPUT.deployment), '\n\tSource: ', validatedEnvVar(INPUT.source), '\n\tTarget: ', validatedEnvVar(INPUT.target), '\n\tRoot File: ', validatedEnvVar(INPUT.rootFile)); - try { - await updateUserConfig( - this.getRequestResource(), - await this.getConvertedFileObject(), - await this.getRequestConfig(), - ); - console.log('Nginx config uploaded successfully!'); - } catch (error) { - taskLib.setResult(taskLib.TaskResult.Failed, error as any); - throw new Error("Nginx config uploading failed!"); - } - }; -} - -const updater = new ConfigUpdater(); -updater.updateNginxConfig(); diff --git a/azure-pipeline/src/task.json b/azure-pipeline/src/task.json deleted file mode 100644 index dde5a62..0000000 --- a/azure-pipeline/src/task.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/Microsoft/azure-pipelines-task-lib/master/tasks.schema.json", - "id": "4b016be0-afe8-4f76-ace3-d7f0355d9605", - "name": "nginx-config-sync-for-azure", - "friendlyName": "NGINX Configuration Sync for Azure", - "description": "This task automates the synchronization of NGINX configuration files from an Azure DevOps repository or GitHub repository to NGINXaaS for Azure deployments.", - "helpMarkDown": "Use this task to seamlessly push NGINX configuration files from your repository to your NGINXaaS for Azure deployments, ensuring consistency and efficiency in your deployment management.", - "category": "Azure Pipelines", - "author": "F5 Networks, Inc.", - "version": { - "Major": 1, - "Minor": 0, - "Patch": 0 - }, - "instanceNameFormat": "Echo $(samplestring)", - "inputs": [ - { - "name": "azureSubscriptionEndpoint", - "aliases": [ - "serviceConnectionName" - ], - "type": "connectedService:AzureRM", - "label": "Azure Service Connection Name", - "helpMarkDown": "Specify the Azure Service Connection to use for authentication.", - "required": true - }, - { - "name": "resourceGroupName", - "type": "string", - "label": "Azure Resource Group Name", - "defaultValue": "", - "required": true, - "helpMarkDown": "Enter the name of the Azure Resource Group where the Nginx deployment resides." - }, - { - "name": "subscriptionId", - "type": "string", - "label": "Azure Subscription ID", - "defaultValue": "", - "required": true, - "helpMarkDown": "Enter the ID of the Azure subscription for the deployment." - }, - { - "name": "deploymentName", - "type": "string", - "label": "Deployment Name", - "defaultValue": "", - "required": true, - "helpMarkDown": "Enter a name for the deployment that will receive the Nginx configuration." - }, - { - "name": "configDirectoryInRepo", - "type": "string", - "label": "Configuration Directory in Repository", - "defaultValue": "", - "required": true, - "helpMarkDown": "Enter the relative path to the Nginx configuration directory in the repository." - }, - { - "name": "configDirectoryInDeployment", - "type": "string", - "label": "Configuration Directory in Deployment", - "defaultValue": "/etc/nginx", - "required": true, - "helpMarkDown": "Enter the target path for the Nginx configuration directory in the deployment environment." - }, - { - "name": "rootConfigFileName", - "type": "string", - "label": "Root Configuration File Name", - "defaultValue": "nginx.conf", - "required": true, - "helpMarkDown": "Enter the name of the root configuration file, such as 'nginx.conf', within the configuration directory." - } - ], - "execution": { - "Node10": { - "target": "dist/index.js" - } - } -} \ No newline at end of file diff --git a/azure-pipeline/src/test/configs/multi/nginx.conf b/azure-pipeline/src/test/configs/multi/nginx.conf deleted file mode 100644 index f664796..0000000 --- a/azure-pipeline/src/test/configs/multi/nginx.conf +++ /dev/null @@ -1,19 +0,0 @@ -http { - upstream app { - zone app 64k; - least_conn; - server 10.0.1.4:8000; - } - - server { - listen 80; - server_name *.this_is_a_ultimate_success.com; - - location / { - include /etc/nginx/conf.d/proxy.conf; - proxy_pass http://app; - health_check; - } - - } -} \ No newline at end of file diff --git a/azure-pipeline/src/test/configs/single/nginx.conf b/azure-pipeline/src/test/configs/single/nginx.conf deleted file mode 100644 index 0874bf1..0000000 --- a/azure-pipeline/src/test/configs/single/nginx.conf +++ /dev/null @@ -1,9 +0,0 @@ -http { - server { - listen 80; - location / { - default_type text/html; - return 200 'hello world!'; - } - } -} \ No newline at end of file diff --git a/azure-pipeline/src/test/update-user-config.test.ts b/azure-pipeline/src/test/update-user-config.test.ts deleted file mode 100644 index 397279f..0000000 --- a/azure-pipeline/src/test/update-user-config.test.ts +++ /dev/null @@ -1,85 +0,0 @@ -import axios from 'axios'; -import { updateUserConfig, ResourceInfo, UserConfig } from '../utils/api'; - -jest.mock('axios'); - -const mockedAxios = axios as jest.Mocked; - -const resource: ResourceInfo = { - subscriptionId: 'subscriptionId', - resourceGroupName: 'resourceGroupName', - deploymentName: 'deploymentName', - apiVersion: 'apiVersion', -}; - -const nginxConfig: UserConfig = { - properties: { - rootFile: '/etc/nginx/nginx.conf', - files: [ - { - virtualPath: '/etc/nginx/conf.d/default.conf', - content: '...', - }, - ], - }, -}; - -const defaultConfig = { - baseURL: 'https://management.azure.com', - headers: { - 'Content-Type': 'application/json; charset=utf-8', - 'Accept': 'application/json', - 'X-Requested-With': 'XMLHttpRequest', - 'Access-Control-Allow-Credentials': true, - }, -}; - -describe('updateUserConfig', () => { - it('sends a PUT request with correct arguments', async () => { - // Arrange - const mockResponse = { data: {} }; - mockedAxios.put.mockResolvedValue(mockResponse); - - // Act - await updateUserConfig(resource, nginxConfig); - - // Assert - expect(mockedAxios.put).toHaveBeenCalledWith( - `/subscriptions/${resource.subscriptionId}/resourceGroups/${resource.resourceGroupName}/providers/NGINX.NGINXPLUS/nginxDeployments/${resource.deploymentName}/configurations/default?api-version=${resource.apiVersion}`, - nginxConfig, - defaultConfig, - ); - }); - - it('throws an error when the request fails', async () => { - // Arrange - const mockError = new Error('Request failed'); - mockedAxios.put.mockRejectedValue(mockError); - - // Act and Assert - await expect(updateUserConfig(resource, nginxConfig)).rejects.toThrow('Request failed'); - }); - - it('throws an error when the resource argument is missing', async () => { - // Act and Assert - await expect(updateUserConfig({} as any, nginxConfig)).rejects.toThrow(); - }); - - it('throws an error when the nginxConfig argument is missing', async () => { - // Act and Assert - await expect(updateUserConfig(resource, {} as any)).rejects.toThrow(); - }); - - it('throws an error when the resource argument has missing properties', async () => { - // Arrange - const incompleteResource: Partial = { - subscriptionId: 'subscriptionId', - resourceGroupName: 'resourceGroupName', - // deploymentName is missing - apiVersion: 'apiVersion', - }; - - // Act and Assert - await expect(updateUserConfig(incompleteResource as ResourceInfo, nginxConfig)).rejects.toThrow(); - }); -}); diff --git a/azure-pipeline/src/utils/api.ts b/azure-pipeline/src/utils/api.ts deleted file mode 100644 index 6aaf880..0000000 --- a/azure-pipeline/src/utils/api.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { http } from "./http"; -import { AxiosRequestConfig } from "axios"; - -export type UserConfig = { - properties: { - rootFile: string; - files?: NginxConfigurationFile[]; - package?: NginxConfigurationPackage; - }; -}; - -export type NginxConfigurationFile = { - virtualPath: string; - content: string; -} - -export type NginxConfigurationPackage = { - data: string; -} - -export type ResourceInfo = { - subscriptionId: string; - resourceGroupName: string; - deploymentName: string; - apiVersion: string; -} - - -export const updateUserConfig = async ( - resource: ResourceInfo, - nginxConfig: UserConfig, - config?: AxiosRequestConfig, -): Promise => { - return await http.put( - `/subscriptions/${resource.subscriptionId}/resourceGroups/${resource.resourceGroupName}/providers/NGINX.NGINXPLUS/nginxDeployments/${resource.deploymentName}/configurations/default?api-version=${resource.apiVersion}`, - nginxConfig, config, - ); -}; - - diff --git a/azure-pipeline/src/utils/constants.ts b/azure-pipeline/src/utils/constants.ts deleted file mode 100644 index d9571aa..0000000 --- a/azure-pipeline/src/utils/constants.ts +++ /dev/null @@ -1,16 +0,0 @@ -// TODO: API_VERSION could be changed to user selectable when there are multiple versions online -export const API_VERSION = '2021-05-01-preview'; - -export const SCOPE = 'https://management.azure.com/.default'; - -export const OUTPUT_PATH: string = './uploading-file.tar.gz'; - -export const INPUT = { - connection: 'AZURESUBSCRIPTIONENDPOINT', - resource: 'RESOURCEGROUPNAME', - subscription: 'SUBSCRIPTIONID', - deployment: 'DEPLOYMENTNAME', - source: 'CONFIGDIRECTORYINREPO', - target: 'CONFIGDIRECTORYINDEPLOYMENT', - rootFile: 'ROOTCONFIGFILENAME', -} \ No newline at end of file diff --git a/azure-pipeline/src/utils/file-handler.ts b/azure-pipeline/src/utils/file-handler.ts deleted file mode 100644 index b78e8eb..0000000 --- a/azure-pipeline/src/utils/file-handler.ts +++ /dev/null @@ -1,50 +0,0 @@ -import tar from 'tar'; -import fs from 'fs'; - -/** - * Class FileHandler provides utility functions to handle file operations, - * such as compressing a file and converting it to a Base64 string. -*/ -export class FileHandler { - - /** - * Compresses a file at the given source path using gzip and tar, and writes the compressed file to the specified output path. - * @param sourcePath {string} The path of the source file to compress. - * @param prefix {string} The prefix to use in the compressed file name. - * @param outputPath {string} The path where the compressed file will be saved. - * @returns {Promise} The output path of the compressed file. - * @throws {Error} If an error occurs during compression. - */ - public compressFile = async ( - sourcePath: string, - prefix: string, - outputPath: string, - ) => { - try { - await tar.c( - { - gzip: true, - prefix: prefix, - file: outputPath, - preservePaths: false, - cwd: sourcePath, - }, - ['./'] - ); - } catch (error) { - throw new Error("An error occered while compressing config file"); - } - return outputPath; - - } - - /** - * Reads the file at the given path and converts its content to a Base64 string. - * @param file {string} The path of the file to read. - * @returns {string} The content of the file as a Base64 string. - */ - public convertFileToBase64String = (file: string) => { - const file_buffer = fs.readFileSync(file); - return file_buffer.toString('base64'); - } -} diff --git a/azure-pipeline/src/utils/http.ts b/azure-pipeline/src/utils/http.ts deleted file mode 100644 index d8ca606..0000000 --- a/azure-pipeline/src/utils/http.ts +++ /dev/null @@ -1,40 +0,0 @@ -import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'; - -const headers = { - Accept: 'application/json', - 'Content-Type': 'application/json; charset=utf-8', - 'Access-Control-Allow-Credentials': true, - 'X-Requested-With': 'XMLHttpRequest', -}; - -const baseURL = 'https://management.azure.com'; - -/** - * Class SimplifiedHttp provides a simplified interface for HTTP requests. - * It encapsulates common configurations and error handling for making HTTP PUT requests. - */ -class SimplifiedHttp { - /** - * Makes an HTTP PUT request to the specified endpoint with the given data and configuration. - * @param endpoint {string} The URL of the endpoint to send the request to. - * @param data {T} Optional data to be sent in the request body. - * @param config {AxiosRequestConfig} Optional configuration object for the request. - * @returns {Promise} A promise that resolves to the response of the request. - * @throws {any} If an error occurs during the request. - */ - put>(endpoint: string, data?: T, config?: AxiosRequestConfig): Promise { - const finalConfig = { - ...config, - baseURL, - headers: { ...headers, ...(config?.headers ?? {}) }, - }; - - return axios.put(endpoint, data, finalConfig) - .catch((error: any) => { - console.error('Error updating user config:', error); - return Promise.reject(error); - }); - } -} - -export const http = new SimplifiedHttp(); diff --git a/azure-pipeline/src/utils/validation.ts b/azure-pipeline/src/utils/validation.ts deleted file mode 100644 index a42ad99..0000000 --- a/azure-pipeline/src/utils/validation.ts +++ /dev/null @@ -1,19 +0,0 @@ -import tl = require('azure-pipelines-task-lib/task'); -import { INPUT } from './constants'; - -export const validatedEnvVar = (inputName: string) => { - const value = tl.getInput(inputName, true); - if (!value) { - throw new Error(`Variable ${inputName} not found!`); - } - return value; -} - -export const validatedAuthParams = (inputName: string) => { - const serviceConnectionName = validatedEnvVar(INPUT.connection); - const value = tl.getEndpointAuthorizationParameter(serviceConnectionName, inputName, false) - if (!value) { - throw new Error(`Input variable ${inputName} not found!`); - } - return value; -} diff --git a/azure-pipeline/tsconfig.json b/azure-pipeline/tsconfig.json deleted file mode 100644 index 9a2fafa..0000000 --- a/azure-pipeline/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "compilerOptions": { - "target": "es6", - "module": "commonjs", - "outDir": "src/dist", - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true, - "sourceMap": true - } -} diff --git a/azure-pipeline/vss-extension.json b/azure-pipeline/vss-extension.json deleted file mode 100644 index 219c131..0000000 --- a/azure-pipeline/vss-extension.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "manifestVersion": 1, - "id": "nginxaas-for-azure-config-sync", - "name": "NGINX Configuration Sync for Azure", - "version": "1.0.0", - "publisher": "F5 Networks, Inc.", - "targets": [ - { - "id": "Microsoft.VisualStudio.Services" - } - ], - "description": "This extension provides a streamlined solution for synchronizing NGINX configuration files with Azure deployments. Designed to support Azure DevOps pipelines, it automates the process of packaging and uploading NGINX configurations to Azure, enhancing efficiency, version control, and security. Ideal for continuous integration and deployment workflows, it simplifies the management of NGINXaaS configurations within the Azure ecosystem.", - "categories": [ - "Azure Pipelines" - ], - "icons": { - "default": "images/extension-icon.png" - }, - "files": [ - { - "path": "src" - } - ], - "content": { - "details": { - "path": "readme.md" - } - }, - "contributions": [ - { - "id": "custom-build-release-task", - "type": "ms.vss-distributed-task.task", - "targets": [ - "ms.vss-distributed-task.tasks" - ], - "properties": { - "name": "nginx-for-azure-config-sync" - } - } - ], - "galleryFlags": [ - "Public" - ], - "public": true, - "tags":[ - "Build task", - "Deploy task" - ] -} \ No newline at end of file diff --git a/github-action/README.md b/github-action/README.md deleted file mode 100644 index 6076693..0000000 --- a/github-action/README.md +++ /dev/null @@ -1,200 +0,0 @@ -# NGINXaaS for Azure Deployment Action - -This action supports managing the configuration of an [NGINXaaS for Azure](https://docs.nginx.com/nginxaas/azure/quickstart/overview/) deployment in a GitHub repository. It enables continuous deployment through GitHub workflows to automatically update the NGINXaaS for Azure deployment when changes are made to the NGINX configuration files stored in the repository. Additionally, one can update NGINX certificates that are already present in Azure key vault. - -## Connecting to Azure - -This action leverages the [Azure Login](https://github.com/marketplace/actions/azure-login) action for authenticating with Azure and performing update to an NGINXaaS for Azure deployment. Two different ways of authentication are supported: -- [Service principal with secrets](https://docs.microsoft.com/en-us/azure/developer/github/connect-from-azure?tabs=azure-portal%2Cwindows#use-the-azure-login-action-with-a-service-principal-secret) -- [OpenID Connect](https://docs.microsoft.com/en-us/azure/developer/github/connect-from-azure?tabs=azure-portal%2Cwindows#use-the-azure-login-action-with-openid-connect) (OIDC) with an Azure service principal using a Federated Identity Credential - -### Sample workflow that authenticates with Azure using an Azure service principal with a secret - -```yaml -# File: .github/workflows/nginxForAzureDeploy.yml - -name: Sync the NGINX configuration from the GitHub repository to an NGINXaaS for Azure deployment -on: - push: - branches: - - main - paths: - - config/** - -jobs: - Deploy-NGINX-Configuration: - runs-on: ubuntu-latest - steps: - - name: 'Checkout repository' - uses: actions/checkout@v2 - - - name: 'Run Azure Login using an Azure service principal with a secret' - uses: azure/login@v2 - with: - creds: ${{ secrets.AZURE_CREDENTIALS }} - - - name: 'Sync the NGINX configuration from the GitHub repository to the NGINXaaS for Azure deployment' - uses: nginxinc/nginx-for-azure-deploy-action/github-action@v0.4.0 - with: - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - resource-group-name: ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} - nginx-deployment-name: ${{ secrets.NGINX_DEPLOYMENT_NAME }} - nginx-config-directory-path: config/ - nginx-root-config-file: nginx.conf - transformed-nginx-config-directory-path: /etc/nginx/ - debug: false -``` - -### Sample workflow that authenticates with Azure using OIDC - -```yaml -# File: .github/workflows/nginxForAzureDeploy.yml - -name: Sync the NGINX configuration from the GitHub repository to an NGINXaaS for Azure deployment -on: - push: - branches: - - main - paths: - - config/** - -permissions: - id-token: write - contents: read - -jobs: - Deploy-NGINX-Configuration: - runs-on: ubuntu-latest - steps: - - name: 'Checkout repository' - uses: actions/checkout@v2 - - - name: 'Run Azure Login using OIDC' - uses: azure/login@v2 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - - name: 'Sync the NGINX configuration from the GitHub repository to the NGINXaaS for Azure deployment' - uses: nginxinc/nginx-for-azure-deploy-action/github-action@v0.4.0 - with: - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - resource-group-name: ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} - nginx-deployment-name: ${{ secrets.NGINX_DEPLOYMENT_NAME }} - nginx-config-directory-path: config/ - nginx-root-config-file: nginx.conf - transformed-nginx-config-directory-path: /etc/nginx/ - debug: false -``` - -> **Note:** -The service principal being used for authenticating with Azure should have access to manage the NGINXaaS deployment. For simplicity, this guide assumes that the service principal has `Contributor` role to manage the deployment. Refer [prerequisites](https://docs.nginx.com/nginxaas/azure/getting-started/prerequisites/) for details. - -## Handling NGINX configuration file paths - -To facilitate the migration of the existing NGINX configuration, NGINXaaS for Azure supports multiple-files configuration with each file uniquely identified by a file path, just like how NGINX configuration files are created and used in a self-hosting machine. An NGINX configuration file can include another file using the [include directive](https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/). The file path used in an `include` directive can either be an absolute path or a relative path to the [prefix path](https://www.nginx.com/resources/wiki/start/topics/tutorials/installoptions/). - -The following example shows two NGINX configuration files inside the `/etc/nginx` directory on disk are copied and stored in a GitHub repository under its `config` directory. - -| File path on disk | File path in the repository | -| ------------------------------------ | --------------------------------- | -| /etc/nginx/nginx.conf | /config/nginx.conf | -| /etc/nginx/sites-enabled/mysite.conf | /config/sites-enabled/mysite.conf | - -To use this action to sync the configuration files from this example, the directory path relative to the GitHub repository root `config/` is set to the action's input `nginx-config-directory-path` for the action to find and package the configuration files. The root file `nginx.conf` is set to the input `nginx-root-config-file`. - -```yaml - - name: 'Sync the NGINX configuration from the GitHub repository to the NGINXaaS for Azure deployment' - uses: nginxinc/nginx-for-azure-deploy-action/github-action@v0.4.0 - with: - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - resource-group-name: ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} - nginx-deployment-name: ${{ secrets.NGINX_DEPLOYMENT_NAME }} - nginx-config-directory-path: config/ - nginx-root-config-file: nginx.conf - debug: false -``` - -By default, the action uses a file's relative path to `nginx-config-directory-path` in the repository as the file path in the NGINXaaS for Azure deployment. - -| File path on disk | File path in the repository | File path in the NGINXaaS for Azure deployment | -| ------------------------------------ | --------------------------------- | ---------------------------------------------- | -| /etc/nginx/nginx.conf | /config/nginx.conf | nginx.conf | -| /etc/nginx/sites-enabled/mysite.conf | /config/sites-enabled/mysite.conf | sites-enabled/mysite.conf | - -The default file path handling works for the case of using relative paths in `include` directives, for example, if the root `nginx.conf` references `mysite.conf` using: - -``` -include sites-enabled/mysite.conf; -``` - -For the case of using absolute paths in `include` directives, for example, if the root `nginx.conf` references `mysite.conf` using: - -``` -include /etc/nginx/sites-enabled/mysite.conf; -``` - -The action supports an optional input `transformed-nginx-config-directory-path` to transform the absolute path of the configuration directory in the NGINXaaS for Azure deployment. The absolute configuration directory path on disk `/etc/nginx/` can be set to `transformed-nginx-config-directory-path` as follows to ensure the configuration files using absolute paths in `include` directives work as expected in the NGINXaaS for Azure deployment. - -```yaml - - name: 'Sync the NGINX configuration from the Git repository to the NGINXaaS for Azure deployment' - uses: nginxinc/nginx-for-azure-deploy-action/github-action@v0.4.0 - with: - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - resource-group-name: ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} - nginx-deployment-name: ${{ secrets.NGINX_DEPLOYMENT_NAME }} - nginx-config-directory-path: config/ - nginx-root-config-file: nginx.conf - transformed-nginx-config-directory-path: /etc/nginx/ - debug: false -``` -The transformed paths of the two configuration files in the NGINXaaS for Azure deployment are summarized in the following table - -| File path on disk | File path in the repository | File path in the NGINXaaS for Azure deployment | -| ------------------------------------ | --------------------------------- | ---------------------------------------------- | -| /etc/nginx/nginx.conf | /config/nginx.conf | /etc/nginx/nginx.conf | -| /etc/nginx/sites-enabled/mysite.conf | /config/sites-enabled/mysite.conf | /etc/nginx/sites-enabled/mysite.conf | - -## Handling NGINX certificates - -Since certificates are secrets, it is assumed they are stored in Azure key vault. One can provide multiple certificate entries to the github action as an array of JSON objects with keys: - -`certificateName`- A unique name for the certificate entry - -`keyvaultSecret`- The secret ID for the certificate on Azure key vault - -`certificateVirtualPath`- This path must match one or more ssl_certificate directive file arguments in your Nginx configuration; and must be unique between certificates within the same deployment - -`keyVirtualPath`- This path must match one or more ssl_certificate_key directive file arguments in your Nginx configuration; and must be unique between certificates within the same deployment - -See the example below - -```yaml -- name: "Sync NGINX certificates to NGINXaaS for Azure" - uses: nginxinc/nginx-for-azure-deploy-action/github-action@v0.4.0 - with: - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - resource-group-name: ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} - nginx-deployment-name: ${{ secrets.NGINX_DEPLOYMENT_NAME }} - nginx-deployment-location: ${{ secrets.NGINX_DEPLOYMENT_LOCATION }} - nginx-certificates: '[{"certificateName": "$NGINX_CERT_NAME", "keyvaultSecret": "https://$NGINX_VAULT_NAME.vault.azure.net/secrets/$NGINX_CERT_NAME", "certificateVirtualPath": "/etc/nginx/ssl/my-cert.crt", "keyVirtualPath": "/etc/nginx/ssl/my-cert.key" } ]' - debug: false -``` - -## Handling NGINX configuration and certificates - -```yaml - - name: "Sync NGINX configuration- multi file and certificate to NGINXaaS for Azure" - uses: nginxinc/nginx-for-azure-deploy-action/github-action@v0.4.0 - with: - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - resource-group-name: ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} - nginx-deployment-name: ${{ secrets.NGINX_DEPLOYMENT_NAME }} - nginx-deployment-location: ${{ secrets.NGINX_DEPLOYMENT_LOCATION }} - nginx-config-directory-path: config/ - nginx-root-config-file: nginx.conf - transformed-nginx-config-directory-path: /etc/nginx/ - nginx-certificates: '[{"certificateName": "$NGINX_CERT_NAME", "keyvaultSecret": "https://$NGINX_VAULT_NAME.vault.azure.net/secrets/$NGINX_CERT_NAME", "certificateVirtualPath": "/etc/nginx/ssl/my-cert.crt", "keyVirtualPath": "/etc/nginx/ssl/my-cert.key" } ]' - debug: false -``` diff --git a/github-action/src/deploy-config.sh b/github-action/src/deploy-config.sh deleted file mode 100755 index 5d1a6e0..0000000 --- a/github-action/src/deploy-config.sh +++ /dev/null @@ -1,183 +0,0 @@ -#!/bin/bash -set -euo pipefail -IFS=$'\n\t' - -transformed_config_dir_path='' -for i in "$@" -do -case $i in - --subscription_id=*) - subscription_id="${i#*=}" - shift - ;; - --resource_group_name=*) - resource_group_name="${i#*=}" - shift - ;; - --nginx_deployment_name=*) - nginx_deployment_name="${i#*=}" - shift - ;; - --config_dir_path=*) - config_dir_path="${i#*=}" - shift - ;; - --root_config_file=*) - root_config_file="${i#*=}" - shift - ;; - --transformed_config_dir_path=*) - transformed_config_dir_path="${i#*=}" - shift - ;; - --debug=*) - debug="${i#*=}" - shift - ;; - *) - echo "Not matched option '${i#*=}' passed in." - exit 1 - ;; -esac -done - -if [[ ! -v subscription_id ]]; -then - echo "Please set 'subscription-id' ..." - exit 1 -fi -if [[ ! -v resource_group_name ]]; -then - echo "Please set 'resource-group-name' ..." - exit 1 -fi -if [[ ! -v nginx_deployment_name ]]; -then - echo "Please set 'nginx-deployment-name' ..." - exit 1 -fi -if [[ ! -v config_dir_path ]]; -then - echo "Please set 'nginx-config-directory-path' ..." - exit 1 -fi -if [[ ! -v root_config_file ]]; -then - echo "Please set 'nginx-root-config-file' ..." - exit 1 -fi - -# Validation and preprocessing - -if [[ "$config_dir_path" = /* ]] -then - echo "The NGINX configuration directory path in the repository '$config_dir_path' must be a relative path." - exit 1 -elif [[ ! "$config_dir_path" = */ ]] -then - echo "The NGINX configuration directory path '$config_dir_path' does not end with '/'. Appending a trailing '/'." - config_dir_path="$config_dir_path/" -fi - -if [[ -d "$config_dir_path" ]] -then - echo "The NGINX configuration directory '$config_dir_path' was found." -else - echo "The NGINX configuration directory '$config_dir_path' does not exist." - exit 1 -fi - -if [[ "$root_config_file" = /* ]] -then - echo "The NGINX configuration root file path '$root_config_file' must be a relative path to the NGINX configuration directory." - exit 1 -fi - -# Remove the leading './' from the root configuration file path if any. -root_config_file=${root_config_file/#'./'/} - -root_config_file_repo_path="$config_dir_path$root_config_file" -if [[ -f "$root_config_file_repo_path" ]] -then - echo "The root NGINX configuration file '$root_config_file_repo_path' was found." -else - echo "The root NGINX configuration file '$root_config_file_repo_path' does not exist." - exit 1 -fi - -if [[ -n "$transformed_config_dir_path" ]] -then - if [[ ! "$transformed_config_dir_path" = /* ]] - then - echo "The specified transformed NGINX configuration directory path '$transformed_config_dir_path' must be an absolute path that starts with '/'." - exit 1 - elif [[ ! "$transformed_config_dir_path" = */ ]] - then - echo "The specified transformed NGINX configuration directory path '$transformed_config_dir_path' does not end with '/'. Appending a trailing '/'." - transformed_config_dir_path="$transformed_config_dir_path/" - fi -fi - -transformed_root_config_file_path="$transformed_config_dir_path$root_config_file" -echo "The transformed root NGINX configuration file path is '$transformed_root_config_file_path'." - -# Create a NGINX configuration tarball. - -config_tarball="nginx-config.tar.gz" - -echo "Creating a tarball from the NGINX configuration directory." -tar -cvzf "$config_tarball" -C "$config_dir_path" --xform s:'./':"$transformed_config_dir_path": . -echo "Successfully created the tarball from the NGINX configuration directory." - -echo "Listing the NGINX configuration file paths in the tarball." -tar -tf "$config_tarball" - -encoded_config_tarball=$(base64 "$config_tarball") - -if [[ "$debug" == true ]]; then - echo "The base64 encoded NGINX configuration tarball" - echo "$encoded_config_tarball" -fi -echo "" - -# Synchronize the NGINX configuration tarball to the NGINXaaS for Azure deployment. - -uuid="$(cat /proc/sys/kernel/random/uuid)" -template_file="template-$uuid.json" -template_deployment_name="${nginx_deployment_name:0:20}-$uuid" - -wget -O "$template_file" https://raw.githubusercontent.com/nginxinc/nginx-for-azure-deploy-action/487d1394d6115d4f42ece6200cbd20859595557d/src/nginx-for-azure-configuration-template.json -echo "Downloaded the ARM template for synchronizing NGINX configuration." -cat "$template_file" -echo "" - -echo "Synchronizing NGINX configuration" -echo "Subscription ID: $subscription_id" -echo "Resource group name: $resource_group_name" -echo "NGINXaaS for Azure deployment name: $nginx_deployment_name" -echo "ARM template deployment name: $template_deployment_name" -echo "" - -az account set -s "$subscription_id" --verbose - -az_cmd=( - "az" - "deployment" - "group" - "create" - "--name" "$template_deployment_name" - "--resource-group" "$resource_group_name" - "--template-file" "$template_file" - "--parameters" - "nginxDeploymentName=$nginx_deployment_name" - "rootFile=$transformed_root_config_file_path" - "tarball=$encoded_config_tarball" - "--verbose" -) - -if [[ "$debug" == true ]]; then - az_cmd+=("--debug") - echo "${az_cmd[@]}" -fi - -"${az_cmd[@]}" diff --git a/github-action/src/nginx-for-azure-configuration-template.json b/github-action/src/nginx-for-azure-configuration-template.json deleted file mode 100644 index 9d1064c..0000000 --- a/github-action/src/nginx-for-azure-configuration-template.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "nginxDeploymentName": { - "type": "string", - "metadata": { - "description": "The name of the NGINXaaS for Azure deployment to synchronize the configuration." - } - }, - "rootFile": { - "type": "string", - "defaultValue": "nginx.conf", - "metadata": { - "description": "The file path of the root NGINX configuration file." - } - }, - "tarball": { - "type": "string", - "metadata": { - "description": "The based64 encoded NGINX configuration tarball." - } - } - }, - "variables": {}, - "resources": [ - { - "type": "NGINX.NGINXPLUS/nginxDeployments/configurations", - "apiVersion": "2024-11-01-preview", - "name": "[concat(parameters('nginxDeploymentName'), '/default')]", - "properties": { - "rootFile": "[parameters('rootFile')]", - "package": { - "data": "[parameters('tarball')]" - } - } - } - ] -} diff --git a/github-action/test/configs/multi/conf.d/proxy.conf b/github-action/test/configs/multi/conf.d/proxy.conf deleted file mode 100644 index c4b8b59..0000000 --- a/github-action/test/configs/multi/conf.d/proxy.conf +++ /dev/null @@ -1,8 +0,0 @@ -proxy_set_header Host $host; -proxy_set_header X-Real-IP $remote_addr; -proxy_set_header X-Proxy-App app; -proxy_set_header Github-Run-Id 000000; -proxy_buffering on; -proxy_buffer_size 4k; -proxy_buffers 8 8k; -proxy_read_timeout 60s; \ No newline at end of file diff --git a/github-action/src/deploy-certificate.sh b/src/deploy-certificate.sh similarity index 100% rename from github-action/src/deploy-certificate.sh rename to src/deploy-certificate.sh diff --git a/src/deploy-config.sh b/src/deploy-config.sh new file mode 100755 index 0000000..ce03ffb --- /dev/null +++ b/src/deploy-config.sh @@ -0,0 +1,332 @@ +#!/bin/bash +set -exo pipefail +IFS=$'\n\t' + +transformed_config_dir_path='' +for i in "$@" +do +case $i in + --subscription-id=*) + subscription_id="${i#*=}" + shift + ;; + --resource-group-name=*) + resource_group_name="${i#*=}" + shift + ;; + --nginx-deployment-name=*) + nginx_deployment_name="${i#*=}" + shift + ;; + --nginx-config-directory-path=*) + config_dir_path="${i#*=}" + shift + ;; + --nginx-root-config-file=*) + root_config_file="${i#*=}" + shift + ;; + --transformed-nginx-config-directory-path=*) + transformed_config_dir_path="${i#*=}" + shift + ;; + --debug=*) + debug="${i#*=}" + shift + ;; + --protected-files=*) + protected_files="${i#*=}" + shift + ;; + *) + echo "Unknown option '${i}' passed in." + exit 1 + ;; +esac +done + +# Validate Required Parameters +missing_params=() +if [ -z "$subscription_id" ]; then + missing_params+=("subscription-id") +fi +if [ -z "$resource_group_name" ]; then + missing_params+=("resource-group-name") +fi +if [ -z "$nginx_deployment_name" ]; then + missing_params+=("nginx-deployment-name") +fi +if [ -z "$config_dir_path" ]; then + missing_params+=("nginx-config-directory-path") +fi +if [ -z "$root_config_file" ]; then + missing_params+=("nginx-root-config-file") +fi + +# Check and print if any required params are missing +if [ ${#missing_params[@]} -gt 0 ]; then + echo "Error: Missing required variables in the workflow:" + echo "${missing_params[*]}" + exit 1 +fi + +# Validation and preprocessing + +if [[ "$config_dir_path" = /* ]] +then + echo "The NGINX configuration directory path in the repository '$config_dir_path' must be a relative path." + exit 1 +elif [[ ! "$config_dir_path" = */ ]] +then + echo "The NGINX configuration directory path '$config_dir_path' does not end with '/'. Appending a trailing '/'." + config_dir_path="$config_dir_path/" +fi + +if [[ -d "$config_dir_path" ]] +then + echo "The NGINX configuration directory '$config_dir_path' was found." +else + echo "The NGINX configuration directory '$config_dir_path' does not exist." + exit 1 +fi + +if [[ "$root_config_file" = /* ]] +then + echo "The NGINX configuration root file path '$root_config_file' must be a relative path to the NGINX configuration directory." + exit 1 +fi + +# Remove the leading './' from the root configuration file path if any. +root_config_file=${root_config_file/#'./'/} + +root_config_file_repo_path="$config_dir_path$root_config_file" +if [[ -f "$root_config_file_repo_path" ]] +then + echo "The root NGINX configuration file '$root_config_file_repo_path' was found." +else + echo "The root NGINX configuration file '$root_config_file_repo_path' does not exist." + exit 1 +fi + +if [[ -n "$transformed_config_dir_path" ]] +then + if [[ ! "$transformed_config_dir_path" = /* ]] + then + echo "The specified transformed NGINX configuration directory path '$transformed_config_dir_path' must be an absolute path that starts with '/'." + exit 1 + elif [[ ! "$transformed_config_dir_path" = */ ]] + then + echo "The specified transformed NGINX configuration directory path '$transformed_config_dir_path' does not end with '/'. Appending a trailing '/'." + transformed_config_dir_path="$transformed_config_dir_path/" + fi +fi + +transformed_root_config_file_path="$transformed_config_dir_path$root_config_file" +echo "The transformed root NGINX configuration file path is '$transformed_root_config_file_path'." + +# Common utility functions + +# Function to trim whitespace from a string +trim_whitespace() { + local var="$1" + # Trim leading whitespace from the file path (var) + # ${var%%[![:space:]]*} starts at the file path's end + # and finds the longest match of non-whitespace + # characters leaving only leading whitespaces + # ${var#"..." } removes the leading whitespace found + var="${var#"${var%%[![:space:]]*}"}" + # Remove trailing whitespace + # See explanation above. The process is reversed here. + var="${var%"${var##*[![:space:]]}"}" + # Check if the file exists in the repository + echo "$var" +} + +# Function to encode file content to base64 +encode_file_base64() { + local file_path="$1" + # Use base64 to encode the file content + # -w 0 option is used to avoid line wrapping in the output + base64 -w 0 "$file_path" +} + +# Function to build virtual path from relative path +build_virtual_path() { + local relative_path="$1" + echo "${transformed_config_dir_path}${relative_path}" +} + +# Function to add a file entry to a JSON array +# The add_file_to_json_array function uses indirect variable references +# and global assignment to update JSON arrays and flags that track +# which files have been processed. The variable names for the JSON array +# and the "first file" flag are passed as arguments, allowing the +# function to generically update different arrays +# (for regular and protected files) without hardcoding their names. +# The syntax ${!var} retrieves the value of the variable whose +# name is stored in 'var', and declare -g ensures the updated +# values are set globally, so changes persist outside the function. +add_file_to_json_array() { + local file_path="$1" + local virtual_path="$2" + local file_type="$3" # "regular" or "protected" + local json_var_name="$4" # Variable name to modify + local first_file_var_name="$5" # Variable name for first_file flag + + if [ -f "$file_path" ]; then + echo "Processing $file_type file: $file_path -> $virtual_path" + + # Base64 encode the file content + local file_content_b64 + file_content_b64=$(encode_file_base64 "$file_path") + + # Get current values using indirect variable references + local current_json="${!json_var_name}" + local is_first_file="${!first_file_var_name}" + + # Add comma separator if not the first file + if [ "$is_first_file" = false ]; then + current_json+="," + fi + + # Add the file entry to JSON array + current_json+="{\"content\":\"$file_content_b64\",\"virtual-path\":\"$virtual_path\"}" + + # Update the variables using indirect assignment + declare -g "$json_var_name=$current_json" + declare -g "$first_file_var_name=false" + + if [[ "$debug" == true ]]; then + echo "$file_type file virtual path: $virtual_path" + echo "$file_type file content (base64): ${file_content_b64:0:50}..." + fi + else + echo "Warning: $file_type file '$file_path' not found" + fi +} + +# Process protected files first to build exclusion list +protected_files_list=() +if [ -n "$protected_files" ]; then + IFS=',' read -ra files <<< "$protected_files" + + for file in "${files[@]}"; do + file=$(trim_whitespace "$file") + if [ -n "$file" ]; then + protected_files_list+=("$file") + fi + done +fi + +# Function to check if a file is in the protected files list +is_protected_file() { + local relative_path="$1" + for protected_file in "${protected_files_list[@]}"; do + if [ "$relative_path" = "$protected_file" ]; then + return 0 # File is protected + fi + done + return 1 # File is not protected +} + +# Process all configuration files individually (excluding protected files) + +echo "Processing NGINX configuration files individually." + +# Build the files JSON array +files_json="[" +files_first_file=true + +# Find all files in the config directory and process them (excluding protected files) +while IFS= read -r -d '' file; do + # Get relative path from config directory + relative_path="${file#$config_dir_path}" + + # Skip if this file is in the protected files list + if is_protected_file "$relative_path"; then + echo "Skipping protected file from regular files: $relative_path" + continue + fi + + # Apply transformation to get virtual path + virtual_path=$(build_virtual_path "$relative_path") + + add_file_to_json_array "$file" "$virtual_path" "regular" "files_json" "files_first_file" +done < <(find "$config_dir_path" -type f -print0) + +files_json+="]" + +if [[ "$debug" == true ]]; then + echo "Regular files JSON: $files_json" +fi + +# Process protected files if specified +protected_files_arg="" +if [ -n "$protected_files" ]; then + echo "Processing protected files: $protected_files" + + # Build the protected files JSON array + protected_files_json="[" + protected_first_file=true + IFS=',' read -ra files <<< "$protected_files" + + for file in "${files[@]}"; do + file=$(trim_whitespace "$file") + if [ -n "$file" ]; then + repo_file_path="${config_dir_path}${file}" + virtual_path=$(build_virtual_path "$file") + + add_file_to_json_array "$repo_file_path" "$virtual_path" "protected" "protected_files_json" "protected_first_file" + fi + done + + protected_files_json+="]" + + if [ "$protected_first_file" = false ]; then + protected_files_arg="--protected-files" + if [[ "$debug" == true ]]; then + echo "Protected files JSON: $protected_files_json" + fi + fi +fi + + +# Synchronize the NGINX configuration files to the NGINXaaS for Azure deployment. + +echo "Synchronizing NGINX configuration" +echo "Subscription ID: $subscription_id" +echo "Resource group name: $resource_group_name" +echo "NGINXaaS for Azure deployment name: $nginx_deployment_name" +echo "" + +az account set -s "$subscription_id" --verbose + +echo "Installing the az nginx extension if not already installed." +az extension add --name nginx --allow-preview true + +az_cmd=( + "az" + "nginx" + "deployment" + "configuration" + "update" + "--name" "default" + "--deployment-name" "$nginx_deployment_name" + "--resource-group" "$resource_group_name" + "--root-file" "$transformed_root_config_file_path" + "--files" "$files_json" + "--verbose" +) + +# Add protected files argument if present +if [ -n "$protected_files_arg" ]; then + az_cmd+=("$protected_files_arg") + az_cmd+=("$protected_files_json") +fi + +if [[ "$debug" == true ]]; then + az_cmd+=("--debug") + echo "${az_cmd[@]}" +fi + +"${az_cmd[@]}" diff --git a/github-action/src/deploy.sh b/src/deploy.sh similarity index 100% rename from github-action/src/deploy.sh rename to src/deploy.sh diff --git a/github-action/src/nginx-for-azure-certificate-template.json b/src/nginx-for-azure-certificate-template.json similarity index 100% rename from github-action/src/nginx-for-azure-certificate-template.json rename to src/nginx-for-azure-certificate-template.json diff --git a/azure-pipeline/src/test/configs/multi/conf.d/proxy.conf b/test/configs/multi/conf.d/proxy.conf similarity index 100% rename from azure-pipeline/src/test/configs/multi/conf.d/proxy.conf rename to test/configs/multi/conf.d/proxy.conf diff --git a/github-action/test/configs/multi/nginx.conf b/test/configs/multi/nginx.conf similarity index 100% rename from github-action/test/configs/multi/nginx.conf rename to test/configs/multi/nginx.conf diff --git a/github-action/test/configs/single/nginx.conf b/test/configs/single/nginx.conf similarity index 100% rename from github-action/test/configs/single/nginx.conf rename to test/configs/single/nginx.conf