Have you ever swapped Azure App Service Slots for more than 30 sites?
Swapping multiple Azure App Service Slots can cause some unwanted swapping of App Settings which might cause an incident in the production.
To prevent swapping the App Setting, we need to set SlotSetting
parameters in the key/value of the App Settings, because default SlotSetting
is false.
To be more clear,
-
watch YouTube demo: https://www.youtube.com/watch?v=q5o6twX9gEg
-
Checkout Slide for getting started the idea of this project.
β οΈ WARNING: Using this Actions requires set App Settings and Connection String for modifyingSlotSetting
value in settings, because the Azure API doesn't allow us to modifying onlySlotSetting
value without touching actual value of settings, please run this action is non-production first to be make sure everything works as you expected.
- Prevent unwanted swap app settings between two slots
- Support multiple Azure App Services
- Users can reviews changes all app services app settings before swap
- Users can config which the app setting will be swapped or not.
- Automatically fix the app setting to be sticked with desired slot following config
- Leverage GitHub Features
- GitHub Action Matrix for retryable steps
- GitHub Pull Request review process for protecting unintentionally swap app service.
This GitHub Actions is required to composition multiple GitHub Actions events for using full workflows as see figure:
Participant in the top 10 final round of Microsoft Virtual Hackathon 2022 June 28, 2022, GitHub Actions Theme
Write a full workflows of using this Actions, it requires to using job and specific events for using this GitHub Actions. You can see the example below:
name: Swap Slots
on:
workflow_dispatch:
pull_request:
types: [opened, closed]
branches:
- appsettings
env:
config_dir: ./.github/workflows/configs
jobs:
get-matrix:
runs-on: ubuntu-latest
outputs:
result: ${{ steps.get-matrix.outputs.deployment-matrix }}
steps:
- uses: actions/checkout@v3
- name: Export deployment matrix
id: get-matrix
run: |
node ./index.js test-get-deploy-slots.json
working-directory: ${{ env.config_dir }}
get-slot-settings:
if: github.event_name == 'workflow_dispatch'
name: ${{ format('βοΈ Get Slot | {0} - {1}', matrix.name, matrix.slot) }}
runs-on: ubuntu-latest
needs: [ get-matrix ]
strategy:
matrix:
include: ${{ fromJson(needs.get-matrix.outputs.result) }}
steps:
- uses: actions/checkout@v2
# SP: github action az webapp swap
- uses: azure/login@v1
with:
creds: ${{ secrets.azure_credentials }}
- uses: mildronize/[email protected]
with:
mode: get-deploy-slots
config: ${{ toJson(matrix) }}
create-swap-plan:
needs: [ get-slot-settings ]
name: Create Swap Plan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Create Swap Plan
uses: mildronize/[email protected]
with:
mode: create-swap-plan
token: ${{ secrets.PAT }}
repo: mildronize/actions-az-webapp-swap-demo
set-slot-settings:
if: >-
github.event_name == 'pull_request' &&
github.event.action != 'closed'
name: ${{ format('βοΈ Set Slot | {0} - {1}', matrix.name, matrix.slot) }}
needs: [ get-matrix ]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.get-matrix.outputs.result) }}
steps:
- uses: actions/checkout@v2
# SP: github action az webapp swap
- uses: azure/login@v1
with:
creds: ${{ secrets.azure_credentials }}
- name: set-slot-settings
uses: mildronize/[email protected]
with:
mode: set-deploy-slots
config: ${{ toJson(matrix) }}
swap-slot:
if: >-
github.event_name == 'pull_request' &&
github.event.action == 'closed' &&
github.event.pull_request.merged == true
name: ${{ format('π Swap Slot | {0} - {1}', matrix.name, matrix.slot) }}
needs: [ get-matrix ]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.get-matrix.outputs.result) }}
steps:
- uses: actions/checkout@v2
# SP: github action az webapp swap
- uses: azure/login@v1
with:
creds: ${{ secrets.azure_credentials }}
- name: set-slot-settings
uses: mildronize/[email protected]
with:
mode: swap-slots
config: ${{ toJson(matrix) }}
clean:
name: Clean
needs: [ swap-slot ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: clean
uses: mildronize/[email protected]
with:
mode: clean
token: ${{ secrets.PAT }}
repo: mildronize/actions-az-webapp-swap-demo
close:
if: >-
github.event_name == 'pull_request' &&
github.event.action == 'closed' &&
github.event.pull_request.merged == false
name: close PR
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: clean
uses: mildronize/[email protected]
with:
mode: clean
token: ${{ secrets.PAT }}
repo: mildronize/actions-az-webapp-swap-demo
Write a JSON config file:
[
{
"name": "my-swap-app-test-01",
"resourceGroup": "rg-swap-app-test",
"slot": "staging",
"targetSlot": "production",
"defaultSlotSetting": "true",
"defaultSensitive": "false",
"appSettings": [
{
"name": "data1",
"sensitive": false,
"slotSetting": true
},
{
"name": "data2",
"sensitive": false,
"slotSetting": true
},
{
"name": "data3",
"sensitive": true,
"slotSetting": true
}
],
"connectionStrings": [
{
"name": "data4",
"sensitive": true,
"slotSetting": true
}
]
}
]
- Assign permission
read:org, repo
To handle with service principle, I've suggestion 2 ways based on scenarios:
-
Create a Service Principle and assign Role & Scope at the same time
az ad sp create-for-rbac --name "my-test-app" --role contributor --scopes /subscriptions/9eac942-xxxxxxxxx --sdk-auth
Note: using azure/login GitHub Actions require flag
--sdk-auth
, even this flag is deprecated.To login, using
azure/login
Actions,- uses: azure/login@v1 with: creds: ${{ secrets.azure_credentials }}
Use Case: This will suite with a few resource to handle
-
Create a Service Principle without assigning any role assignment
az ad sp create-for-rbac -n my-test-app --skip-assignment --sdk-auth
Note: using azure/login GitHub Actions require flag
--sdk-auth
, even this flag is deprecated.To support this service principle, the azure/login support Support for using allow-no-subscriptions
To login, using
azure/login
Actions,- uses: azure/login@v1 with: creds: ${{ secrets.azure_credentials }} allow-no-subscriptions: true
Next step, you can assign this Service Principle in a Azure AD group, and assign role assignment (Access Control (IAM)) to the resource that you want to get access:
For example,
To provide Least Privilege for Azure Resources:
Read more:
Create a file role.json
and save the JSON content below:
{
"type": "Microsoft.Authorization/roleDefinitions",
"roleName": "prod-swap-slot",
"description": "Prod Swap Slot",
"assignableScopes": [
"/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
],
"name": "prod-swap-slot",
"roleType": "CustomRole",
"permissions": [
{
"actions": [
"Microsoft.Web/sites/slots/slotsswap/action",
"Microsoft.Web/sites/slots/config/list/Action",
"Microsoft.Web/sites/config/Read",
"Microsoft.Web/sites/config/list/Action",
"Microsoft.Web/sites/config/Write",
"Microsoft.Web/sites/slots/config/Write",
"microsoft.web/sites/slots/operationresults/read"
],
"notActions": [],
"dataActions": [],
"notDataActions": []
}
]
}
Run this command to create a role.
az role definition create --role-definition role.json
Microsoft.Web/sites/slots/config/list/Action
Microsoft.Web/sites/config/Read
Microsoft.Web/sites/config/list/Action
Microsoft.Web/sites/config/Write
Microsoft.Web/sites/slots/config/Write
Microsoft.Web/sites/slots/slotsswap/action
Microsoft.Web/sites/slots/operationresults/read
- Thada Wangthammang (@mildronize)
- Sirinat Paphatsirinatthi (@dmakeroam)
- Piti Champeethong (@ninefyi)
T.T. Software Solution | WRM Software |
- Mask sensitive in log