Skip to content

Commit

Permalink
Merge pull request #9 from IATI/develop
Browse files Browse the repository at this point in the history
Next release
  • Loading branch information
nosvalds authored Aug 11, 2021
2 parents 8fdb5e1 + 9ee2f72 commit c6a619c
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 42 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/apim-backup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Backup_APIM

on:
workflow_dispatch:
schedule:
- cron: "05 3 * * *"

jobs:
backup-apim:
runs-on: ubuntu-latest
strategy:
matrix:
environment: ["dev", "prod"]

steps:
- name: Login via Az module
uses: azure/login@v1
with:
creds: ${{ secrets.PROD_AZURE_CREDENTIALS }}
enable-AzPSSession: true

# Create storage account for the environment first before running:
# New-AzStorageAccount -StorageAccountName "stapimiatibackup{env}" -Location uksouth -ResourceGroupName "rg-apim-{env}" -Type 'Standard_LRS'
- name: "Backup APIM ${{ matrix.environment }} Instance"
uses: azure/powershell@v1
env:
ApimServiceName: apim-iati-${{ matrix.environment }}
BackupName: BackupApimIati${{ matrix.environment }}
BackupResourceGroup: rg-apim-${{ matrix.environment }}
StorageName: stapimiatibackup${{ matrix.environment }}
ContainerName: backup
with:
inlineScript: |
$StorageKey = (Get-AzStorageAccountKey -ResourceGroupName ${{ env.BackupResourceGroup }} -StorageAccountName ${{ env.StorageName }})[0].Value
$StorageContext = New-AzStorageContext -StorageAccountName ${{ env.StorageName }} -StorageAccountKey $StorageKey
$DateTime = Get-Date -Format "dd_MM_yyyy_HHmm"
$BlobName = ("${{ env.ContainerName }}" + "_" + $DateTime)
Backup-AzApiManagement -ResourceGroupName ${{ env.BackupResourceGroup }} -Name ${{ env.ApimServiceName }} -StorageContext $StorageContext -TargetContainerName ${{ env.ContainerName }} -TargetBlobName $BlobName
azPSVersion: "3.1.0"
5 changes: 4 additions & 1 deletion .github/workflows/apim-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ jobs:
repoUrl: https://raw.githubusercontent.com/iati/apim-iati-gateway
repoApimPath: service
RedisConnectionString: ${{ secrets.DEV_REDIS_CONNECTION_STRING }}
ApplicationInsightsInstanceName: appi-apim


steps:
- name: "Checkout GitHub Action"
Expand Down Expand Up @@ -61,4 +63,5 @@ jobs:
--parameters ApimCapacity=${{ env.ApimCapacity }} \
--parameters ApimGatewayHostname=${{ env.ApimGatewayHostname }} \
--parameters ApimDevPortalHostname=${{ env.ApimDevPortalHostname }} \
--parameters RedisConnectionString=${{ env.RedisConnectionString }}
--parameters RedisConnectionString=${{ env.RedisConnectionString }} \
--parameters ApplicationInsightsInstanceName=${{ env.ApplicationInsightsInstanceName }}
4 changes: 3 additions & 1 deletion .github/workflows/apim-develop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jobs:
repoBranch: ${GITHUB_REF##*/}
repoApimPath: service
RedisConnectionString: ${{ secrets.DEV_REDIS_CONNECTION_STRING }}
ApplicationInsightsInstanceName: appi-apim

steps:
- name: "Checkout GitHub Action"
Expand Down Expand Up @@ -61,4 +62,5 @@ jobs:
--parameters ApimCapacity=${{ env.ApimCapacity }} \
--parameters ApimGatewayHostname=${{ env.ApimGatewayHostname }} \
--parameters ApimDevPortalHostname=${{ env.ApimDevPortalHostname }} \
--parameters RedisConnectionString=${{ env.RedisConnectionString }}
--parameters RedisConnectionString=${{ env.RedisConnectionString }} \
--parameters ApplicationInsightsInstanceName=${{ env.ApplicationInsightsInstanceName }}
58 changes: 58 additions & 0 deletions .github/workflows/apim-developer-portal-sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Sync_Developer_Portal_Dev_To_PROD

on:
workflow_dispatch:

jobs:
sync-developer-portal:
runs-on: ubuntu-latest

env:
NODE_VERSION: 14
RESOURCE_GROUP: rg-apim
SERVICE_NAME: apim-iati
SOURCE_ENV: dev
TARGET_ENV: prod
SubscriptionId: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
AZURE_CREDENTIALS: ${{ secrets.PROD_AZURE_CREDENTIALS }}
gtmContainerId: ${{ secrets.GTM_CONTAINER_ID }}

steps:
- name: "Checkout IATI/api-management-developer-portal#master"
uses: actions/checkout@v2
with:
repository: IATI/api-management-developer-portal
ref: master

- name: "Login to Azure"
uses: azure/[email protected]
with:
creds: ${{ env.AZURE_CREDENTIALS }}

- name: "Setup Node ${{ env.NODE_VERSION }} Environment"
uses: actions/[email protected]
with:
node-version: ${{ env.NODE_VERSION }}

- name: "Install Dependencies with Npm"
run: npm install

- name: "Sync Developer Portal from Dev to Prod"
working-directory: ./scripts.v3
run: |
node ./migrate \
--sourceSubscriptionId ${{ env.SubscriptionId }} \
--sourceResourceGroupName ${{ env.RESOURCE_GROUP }}-${{ env.SOURCE_ENV }} \
--sourceServiceName ${{ env.SERVICE_NAME }}-${{ env.SOURCE_ENV }} \
--destSubscriptionId ${{ env.SubscriptionId }} \
--destResourceGroupName ${{ env.RESOURCE_GROUP }}-${{ env.TARGET_ENV }} \
--destServiceName ${{ env.SERVICE_NAME }}-${{ env.TARGET_ENV }}
- name: "Update GTM config on Prod"
working-directory: ./scripts.v3
run: |
node ./gtm \
--subscriptionId ${{ env.SubscriptionId }} \
--resourceGroupName ${{ env.RESOURCE_GROUP }}-${{ env.TARGET_ENV }} \
--serviceName ${{ env.SERVICE_NAME }}-${{ env.TARGET_ENV }} \
--gtmContainerId ${{ env.gtmContainerId }}
5 changes: 4 additions & 1 deletion .github/workflows/apim-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ jobs:
repoBranch: ${GITHUB_REF##*/}
repoApimPath: service
RedisConnectionString: ${{ secrets.PROD_REDIS_CONNECTION_STRING }}
ApplicationInsightsInstanceName: appi-apim


steps:
- name: "Checkout GitHub Action"
Expand Down Expand Up @@ -58,4 +60,5 @@ jobs:
--parameters ApimCapacity=${{ env.ApimCapacity }} \
--parameters ApimGatewayHostname=${{ env.ApimGatewayHostname }} \
--parameters ApimDevPortalHostname=${{ env.ApimDevPortalHostname }} \
--parameters RedisConnectionString=${{ env.RedisConnectionString }}
--parameters RedisConnectionString=${{ env.RedisConnectionString }} \
--parameters ApplicationInsightsInstanceName=${{ env.ApplicationInsightsInstanceName }}
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "extraction_templates/azure-api-management-devops-resource-kit"]
path = extraction_templates/azure-api-management-devops-resource-kit
url = [email protected]:Azure/azure-api-management-devops-resource-kit.git
76 changes: 66 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,77 @@

- [Azure CLI](https://docs.microsoft.com/en-us/dotnet/azure/install-azure-cli)
- [.NET 3.1.0](https://docs.microsoft.com/en-us/dotnet/core/install/)
- [Azure/azure-api-management-devops-resource-kit](https://github.com/Azure/azure-api-management-devops-resource-kit)
- [Example Apim Devops](https://github.com/RvLabsMSFT/rvlabs-apim-devops)

## Extracting

Install Extractor Tool as CLI

```bash
git clone [email protected]:Azure/azure-api-management-devops-resource-kit.git
cd {path_to_folder}/src/APIM_ARMTemplate/apimtemplate
dotnet pack -c Release
dotnet tool install -g --add-source .\bin\Release apimtemplate
# follow instuctions to save in PATH
cd extraction_templates/azure-api-management-devops-resource-kit/src/APIM_ARMTemplate/apimtemplate
dotnet restore
dotnet run extract --extractorConfig ../../../../apimExtract.json
```

Run extractor w/ config
```bash
apim-templates extract --extractorConfig extraction_templates/apimExtract.json
Update the Extractor Tool

- Update the submodule to the latest commit, then do the above

## Developer Portal

The developer portal look and feel customisation cannot be managed with ARM templates in source control.

It can be synced from Dev to PROD using a node script that utilises the Management API behind the scenes.

This is set up in a GitHub Actions Workflow that can be run manually from GitHub

[Workflow On GitHub](https://github.com/IATI/apim-iati-gateway/actions/workflows/apim-developer-portal-sync.yml) > Run Workflow

You can then check that the Developer Portal has been Published in the Portal [here](https://portal.azure.com/#@iatitech.onmicrosoft.com/resource/subscriptions/bcaf7a00-7a14-4932-ac41-7bb0dee0d2a9/resourceGroups/rg-apim-PROD/providers/Microsoft.ApiManagement/service/apim-iati-PROD/apim-portal)

The PROD_AZURE_CREDENTIALS Service Principal has been given Contributor role on both the Dev and Prod Resources so that it can move the resources between them.

## Backup

The APIM instance can be manually backed up by running the `apim-backup.yml` GitHub Actions workflow from GitHub. It is also set to backup the dev and prod instances nightly.

The backup is stored in a blob storage account in the same resource group as the APIM instance that it's backing up.

## Restore

Budget approximately 2-3hrs to complete a restore to a NEW Apim instance.

A backup can be [restored](https://docs.microsoft.com/en-us/powershell/module/az.apimanagement/restore-azapimanagement?view=azps-6.2.1) to a NEW apim instance with the following steps:

- [Install Azure Powershell](https://docs.microsoft.com/en-us/powershell/azure/install-az-ps?view=azps-6.2.1)
- Create a new APIM instance, e.g. `apim-iati-dr` in the appropriate resource group `rg-apim-dr`
- The SKU of the service being restored into must match the SKU of the backed-up service being restored.
- Timing: ~45min
- Create KeyVault Access Policies for the new APIM instance, so it can access `kv-iati-PROD`
- Use the below PowerShell commands to restore from the appropriate backup:
- Example below: source backup is in a storage account `stapimiatibackupprod` in resource group `rg-apim-prod`, target apim instance is `apim-iati-dr` in resource group `rg-apim-dr`
- Change the CNAME records (`developer.iatistandard.org` and `api.iatistandard.org`) in Cloudflare to point to the new APIM instance URL
- Add the Custom Domain in the Azure Portal (doesn't seem to be copied with the backup)
- Restore operation doesn't change custom hostname configuration of the target service.
- Timing: ~20min
- Copy the Developer Portal content from `dev` to your new instance (modify TARGET_ENV in `apim-developer-portal-sync.yml`)

If just restoring to the existing `apim-iati-prod` instance, then only the PowerShell restore command would likely be neccessary.

```pwsh
PS >$storageKey = (Get-AzStorageAccountKey -ResourceGroupName "rg-apim-prod" -StorageAccountName "stapimiatibackupprod")[0].Value
PS >$storageContext = New-AzStorageContext -StorageAccountName "stapimiatibackupprod" -StorageAccountKey $storageKey
PS >Restore-AzApiManagement -ResourceGroupName "rg-apim-dr" -Name "apim-iati-dr" -StorageContext $StorageContext -SourceContainerName "backup" -SourceBlobName "backup_29_07_2021_1535"
```

Take note of considerations [here](https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-disaster-recovery-backup-restore#constraints-when-making-backup-or-restore-request)

- Restore is a long running operation that may take up to 30 or more minutes to complete.
- [Constraints](https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-disaster-recovery-backup-restore#constraints-when-making-backup-or-restore-request)
- [What is not backed up](https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-disaster-recovery-backup-restore#what-is-not-backed-up)

# Resources

- [Azure/azure-api-management-devops-resource-kit](https://github.com/Azure/azure-api-management-devops-resource-kit)
- [Example Apim Devops](https://github.com/RvLabsMSFT/rvlabs-apim-devops)
2 changes: 1 addition & 1 deletion extraction_templates/apimExtract.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"sourceApimName": "apim-iati-dev",
"destinationApimName": "apim-iati-dev",
"resourceGroup": "rg-apim-dev",
"fileFolder": "./service",
"fileFolder": "../../../../../service",
"linkedTemplatesBaseUrl": "https://raw.githubusercontent.com/IATI/apim-iati-gateway/main/service/",
"policyXMLBaseUrl": "https://raw.githubusercontent.com/IATI/apim-iati-gateway/main/service/policies/",
"splitAPIs": "false",
Expand Down
63 changes: 63 additions & 0 deletions service/apim-iati-dev-loggers.template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"ApimServiceName": {
"type": "string"
},
"Environment": {
"type": "string"
},
"ApplicationInsightsInstanceName": {
"type": "string"
}
},
"variables": {
"ApplicationInsightsInstanceNameWithEnv": "[concat(parameters('ApplicationInsightsInstanceName'), '-', parameters('Environment'))]"
},
"resources": [
{
"name": "[variables('ApplicationInsightsInstanceNameWithEnv')]",
"type": "Microsoft.Insights/components",
"apiVersion": "2015-05-01",
"location": "UK South",
"tags": {},
"kind": "other",
"properties": {
"Application_Type": "other"
}
},
{
"properties": {
"loggerType": "applicationInsights",
"description": "Application Insights logger for APIM",
"credentials": {
"instrumentationKey": "[reference(resourceId('Microsoft.Insights/components', variables('ApplicationInsightsInstanceNameWithEnv')), '2015-05-01').InstrumentationKey]"
},
"isBuffered": true,
"resourceId": "[resourceId('Microsoft.Insights/components',variables('ApplicationInsightsInstanceNameWithEnv'))]"
},
"name": "[concat(parameters('ApimServiceName'), '/', variables('ApplicationInsightsInstanceNameWithEnv'))]",
"type": "Microsoft.ApiManagement/service/loggers",
"apiVersion": "2021-01-01-preview"
},
{
"properties": {
"alwaysLog": "allErrors",
"loggerId": "[concat('/loggers/', variables('ApplicationInsightsInstanceNameWithEnv'))]",
"httpCorrelationProtocol": "Legacy",
"logClientIp": true,
"sampling": {
"samplingType": "fixed",
"percentage": 100.0
}
},
"name": "[concat(parameters('ApimServiceName'), '/applicationinsights')]",
"type": "Microsoft.ApiManagement/service/diagnostics",
"apiVersion": "2021-01-01-preview",
"dependsOn": [
"[resourceId('Microsoft.ApiManagement/service/loggers', parameters('ApimServiceName'), variables('ApplicationInsightsInstanceNameWithEnv'))]"
]
}
]
}
44 changes: 29 additions & 15 deletions service/apim-iati-dev-master.template.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,10 @@
"description": "Service url for each Api"
}
},
"NamedValues": {
"type": "object",
"metadata": {
"description": "Named values"
}
},
"ApiLoggerId": {
"type": "object",
"metadata": {
"description": "LoggerId for this api"
}
},
"LoggerResourceId": {
"type": "object",
"ApplicationInsightsInstanceName": {
"type": "string",
"metadata": {
"description": "ResourceId for the logger"
"description": "App Insights name for this apim instance (e.g. appi-apim)"
}
},
"SubscriptionId": {
Expand Down Expand Up @@ -209,6 +197,32 @@
"[resourceId('Microsoft.Resources/deployments', 'apimTemplate')]"
]
},
{
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat(parameters('LinkedTemplatesBaseUrl'), '/apim-iati-dev-loggers.template.json')]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"ApimServiceName": {
"value": "[parameters('ApimServiceName')]"
},
"Environment": {
"value": "[parameters('Environment')]"
},
"ApplicationInsightsInstanceName": {
"value": "[parameters('ApplicationInsightsInstanceName')]"
}
}
},
"name": "loggersTemplate",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2018-05-01",
"dependsOn": [
"[resourceId('Microsoft.Resources/deployments', 'apimTemplate')]"
]
},
{
"properties": {
"mode": "Incremental",
Expand Down
Loading

0 comments on commit c6a619c

Please sign in to comment.