Skip to content

Commit

Permalink
Add ARM implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
arnaud-tincelin committed Jun 28, 2022
1 parent 95cc49a commit 648b8a2
Show file tree
Hide file tree
Showing 10 changed files with 381 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
*.tfvars
.terraform
.terraform*
*.tfstate
*.tfstate*
38 changes: 27 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,43 @@ This example deploys a full environment to support azure devops agents. For each

Note: If there is no agent in the pool, the pipeline will fail. To workaround this limitation, a "placeholder" agent must be configured in order to always have 1 agent in the pool. This agent will be a "fake" agent as it will always be marked as offline.

## PAT required permissions
## Pre-requisites

- `Agent Pools`: `Read & manage` to be able to register / unregister agents
- `Build`: `Read` for Keda to be able to get the queue of pending builds
1. Create an agent pool and save its **name** and **id**.
Note: the pool ID corresponds to the `queueId` from the url when a pool is selected in the browser.
Example: `https://dev.azure.com/myOrg/myProject/_settings/agentqueues?queueId=11&view=jobs`, the pool ID is 11
1. Create a **PAT** (Personal Access Token) with the following permissions:
- `Agent Pools`: `Read & manage` to be able to register / unregister agents
- `Build`: `Read` for Keda to be able to get the queue of pending builds
1. Configure the **max number of parallel jobs** in Azure Devops so that your ACA does scale accordingly

## Deploy the solution

Using Terraform, this solution deploys:
This repository deploys:

- a resource group
- a VNET with 2 subnets to host the container apps
- a container registry to store the agent image + build & publish the docker image
- a log analytics workspace to store logs from the container apps
- a container apps environment + a container apps

To deploy, run the following commands:
There are 2 implementations of the same infrastructure in this repository:

```bash
az login
terraform init
terraform apply -auto-approve
```
- using Terraform in the `terraform` folder:
To deploy, run the following commands:

```bash
az login
terraform init
terraform apply -auto-approve
```

- using ARM template in the `arm` folder:
To deploy, run the following commands:

```bash
./deploy.sh myRG myLocation myACR myWorkspace https://dev.azure.com/myOrg myPoolName myPoolID myToken MaxRunnerCount
```

## Create a "placeholder" agent

Expand All @@ -43,10 +58,11 @@ There are 2 possibilities to deploy a placeholder agent:
cd -
```

- From an azure devops agent pool, click on "New Agent" and follow the instructions. You might need a temporary VM to install the agent. The VM might be deleted once the agent has been registered in the pool.
- From an azure devops agent pool, click on "New Agent" and follow the instructions. You will need a temporary VM to install the agent. The VM might be deleted once the agent has been registered in the pool.

## References

- [Container Apps ARM Template](https://docs.microsoft.com/en-us/azure/templates/microsoft.app/containerapps?tabs=json)
- [Docker Agents for Azure Devops](https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/docker?view=azure-devops#linux)
- [Keda for Azure Pipelines](https://keda.sh/docs/2.5/scalers/azure-pipelines/)
- [Keda](https://keda.sh/docs/2.6/deploy/#yaml)
Expand Down
224 changes: 224 additions & 0 deletions arm/aca.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "String"
},
"workspaceName": {
"type": "string",
"metadata": {
"description": "Name of the log analytics workspace to create."
}
},
"azp_url": {
"type": "String"
},
"azp_token": {
"type": "securestring"
},
"azp_pool": {
"type": "String"
},
"azp_poolID": {
"type": "String"
},
"registry_server": {
"type": "String"
},
"registry_username": {
"type": "String"
},
"registry_password": {
"type": "securestring"
},
"image": {
"type": "String"
},
"min_replicas": {
"type": "Int"
},
"max_replicas": {
"type": "Int"
}
},
"functions": [],
"variables": {},
"resources": [
{
"type": "Microsoft.Network/virtualNetworks",
"apiVersion": "2020-11-01",
"name": "devops-agents",
"location": "[parameters('location')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"10.0.0.0/16"
]
},
"dhcpOptions": {
"dnsServers": []
},
"subnets": [
{
"name": "infrastructure",
"properties": {
"addressPrefix": "10.0.0.0/21",
"serviceEndpoints": [],
"delegations": [],
"privateEndpointNetworkPolicies": "Enabled",
"privateLinkServiceNetworkPolicies": "Enabled"
}
},
{
"name": "runtime",
"properties": {
"addressPrefix": "10.0.8.0/21",
"serviceEndpoints": [],
"delegations": [],
"privateEndpointNetworkPolicies": "Enabled",
"privateLinkServiceNetworkPolicies": "Enabled"
}
}
],
"virtualNetworkPeerings": [],
"enableDdosProtection": false
}
},
{
"type": "microsoft.operationalinsights/workspaces",
"apiVersion": "2021-12-01-preview",
"name": "[parameters('workspaceName')]",
"location": "westeurope",
"properties": {
"sku": {
"name": "pergb2018"
},
"retentionInDays": 30,
"features": {
"enableLogAccessUsingOnlyResourcePermissions": true
},
"workspaceCapping": {
"dailyQuotaGb": -1
},
"publicNetworkAccessForIngestion": "Enabled",
"publicNetworkAccessForQuery": "Enabled"
}
},
{
"type": "Microsoft.App/managedEnvironments",
"apiVersion": "2022-03-01",
"name": "devops-agent",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('microsoft.operationalinsights/workspaces', parameters('workspaceName'))]",
"[resourceId('Microsoft.Network/virtualNetworks', 'devops-agents')]"
],
"properties": {
"appLogsConfiguration": {
"destination": "log-analytics",
"logAnalyticsConfiguration": {
"customerId": "[reference(resourceId('microsoft.operationalinsights/workspaces', parameters('workspaceName')), '2021-12-01-preview').customerId]",
"sharedKey": "[listKeys(resourceId('microsoft.operationalinsights/workspaces', parameters('workspaceName')), '2021-12-01-preview').primarySharedKey]"
}
},
"vnetConfiguration": {
"infrastructureSubnetId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', 'devops-agents', 'infrastructure')]",
"internal": true,
"runtimeSubnetId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', 'devops-agents', 'runtime')]"
}
}
},
{
"type": "Microsoft.App/containerApps",
"apiVersion": "2022-03-01",
"name": "devops-agent",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.App/managedEnvironments', 'devops-agent')]"
],
"identity": {
"type": "SystemAssigned"
},
"properties": {
"managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', 'devops-agent')]",
"configuration": {
"secrets": [
{
"name": "registry-secret",
"value": "[parameters('registry_password')]"
},
{
"name": "azp-token",
"value": "[parameters('azp_token')]"
},
{
"name": "azp-url",
"value": "[parameters('azp_url')]"
}
],
"registries": [
{
"server": "[parameters('registry_server')]",
"username": "[parameters('registry_username')]",
"passwordSecretRef": "registry-secret"
}
]
},
"template": {
"containers": [
{
"image": "[parameters('image')]",
"name": "devops-agent",
"resources": {
"cpu": 1.75,
"memory": "3.5Gi"
},
"env": [
{
"name": "AZP_TOKEN",
"secretRef": "azp-token"
},
{
"name": "AZP_URL",
"secretRef": "azp-url"
},
{
"name": "AZP_POOL",
"value": "[parameters('azp_pool')]"
}
]
}
],
"scale": {
"minReplicas": "[parameters('min_replicas')]",
"maxReplicas": "[parameters('max_replicas')]",
"rules": [
{
"name": "azure-pipelines",
"custom": {
"type": "azure-pipelines",
"metadata": {
"poolID": "[parameters('azp_poolID')]",
"targetPipelinesQueueLength": "1"
},
"auth": [
{
"secretRef": "azp-token",
"triggerParameter": "personalAccessToken"
},
{
"secretRef": "azp-url",
"triggerParameter": "organizationURL"
}
]
}
}
]
}
}
}
}
],
"outputs": {}
}
84 changes: 84 additions & 0 deletions arm/acr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"name": {
"type": "string",
"metadata": {
"description": "Name of the container registry to create."
}
},
"location": {
"type": "string",
"metadata": {
"description": "The location of the resource."
}
}
},
"functions": [],
"variables": {},
"resources": [
{
"type": "Microsoft.ContainerRegistry/registries",
"apiVersion": "2022-02-01-preview",
"name": "[parameters('name')]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard",
"tier": "Standard"
},
"properties": {
"adminUserEnabled": true,
"policies": {
"quarantinePolicy": {
"status": "disabled"
},
"trustPolicy": {
"type": "Notary",
"status": "disabled"
},
"retentionPolicy": {
"days": 7,
"status": "disabled"
},
"exportPolicy": {
"status": "enabled"
},
"azureADAuthenticationAsArmPolicy": {
"status": "enabled"
},
"softDeletePolicy": {
"retentionDays": 7,
"status": "disabled"
}
},
"encryption": {
"status": "disabled"
},
"dataEndpointEnabled": false,
"publicNetworkAccess": "Enabled",
"networkRuleBypassOptions": "AzureServices",
"zoneRedundancy": "Disabled",
"anonymousPullEnabled": false
}
}
],
"outputs": {
"name": {
"type": "string",
"value": "[parameters('name')]"
},
"loginServer": {
"type": "string",
"value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))).loginServer]"
},
"username": {
"type": "string",
"value": "[listCredentials(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').username]"
},
"password": {
"type": "string",
"value": "[listCredentials(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2022-02-01-preview').passwords[0].value]"
}
}
}
Loading

0 comments on commit 648b8a2

Please sign in to comment.