diff --git a/.github/workflows/build-and-push-image-development.yml b/.github/workflows/build-and-push-image-development.yml
new file mode 100644
index 000000000..4d4752909
--- /dev/null
+++ b/.github/workflows/build-and-push-image-development.yml
@@ -0,0 +1,43 @@
+name: Continuous delivery
+
+on:
+ push:
+ branches:
+ - main
+jobs:
+ build-and-push-image-development:
+ name: Build and push image development
+ environment: development
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out code
+ uses: actions/checkout@v3
+
+ - name: Azure Container Registry login
+ uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DEVELOPMENT_AZURE_ACR_CLIENTID }}
+ password: ${{ secrets.DEVELOPMENT_AZURE_ACR_SECRET }}
+ registry: ${{ secrets.DEVELOPMENT_ACR_URL }}
+
+ - name: Prepare tags
+ id: prepare-tags
+ run: |
+ DOCKER_IMAGE=${{ secrets.DEVELOPMENT_ACR_URL }}/academies-academisation-api
+ VERSION=latest
+ TAGS="${DOCKER_IMAGE}:${VERSION}"
+ if [ "${{ github.event_name }}" = "push" ]; then
+ VERSION=sha-${GITHUB_SHA}
+ TAGS="$TAGS,${DOCKER_IMAGE}:${VERSION}"
+ fi
+ echo ::set-output name=tags::${TAGS}
+ echo ::set-output name=deploy-version::${VERSION}
+
+ - name: Push image
+ uses: docker/build-push-action@v3
+ with:
+ context: .
+ file: ./Dockerfile
+ build-args: ASPNET_IMAGE_TAG=6.0.9-bullseye-slim-amd64
+ push: true
+ tags: ${{ steps.prepare-tags.outputs.tags }}
diff --git a/.github/workflows/continuous-integration-terraform.yml b/.github/workflows/continuous-integration-terraform.yml
new file mode 100644
index 000000000..fb222992b
--- /dev/null
+++ b/.github/workflows/continuous-integration-terraform.yml
@@ -0,0 +1,63 @@
+name: Continuous integration
+
+on:
+ push:
+ branches: main
+ pull_request:
+
+jobs:
+ terraform-validate:
+ name: Terraform Validate
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out code
+ uses: actions/checkout@v3
+
+ - name: Check for terraform version mismatch
+ run: |
+ DOTFILE_VERSION=$(cat terraform/.terraform-version)
+ TERRAFORM_IMAGE_REFERENCES=$(grep "uses: docker://hashicorp/terraform" .github/workflows/continuous-integration-terraform.yml | grep -v TERRAFORM_IMAGE_REFERENCES | wc -l | tr -d ' ')
+ if [ "$(grep "docker://hashicorp/terraform:${DOTFILE_VERSION}" .github/workflows/continuous-integration-terraform.yml | wc -l | tr -d ' ')" != "$TERRAFORM_IMAGE_REFERENCES" ]
+ then
+ echo -e "\033[1;31mError: terraform version in .terraform-version file does not match docker://hashicorp/terraform versions in .github/workflows/continuous-integration-terraform.yml"
+ exit 1
+ fi
+
+ - name: Remove azure backend
+ run: rm ./terraform/backend.tf
+
+ - name: Run a Terraform init
+ uses: docker://hashicorp/terraform:1.2.9
+ with:
+ entrypoint: terraform
+ args: -chdir=terraform init
+
+ - name: Run a Terraform validate
+ uses: docker://hashicorp/terraform:1.2.9
+ with:
+ entrypoint: terraform
+ args: -chdir=terraform validate
+
+ - name: Run a Terraform format check
+ uses: docker://hashicorp/terraform:1.2.9
+ with:
+ entrypoint: terraform
+ args: -chdir=terraform fmt -check=true -diff=true
+ terraform-docs-validation:
+ name: Terraform Docs validation
+ needs: terraform-validate
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out code
+ uses: actions/checkout@v3
+ with:
+ ref: ${{ github.event.pull_request.head.ref }}
+
+ - name: Generate Terraform docs
+ uses: terraform-docs/gh-actions@v1.0.0
+ with:
+ working-dir: terraform
+ config-file: .terraform-docs.yml
+ output-file: README.md
+ output-method: inject
+ fail-on-diff: true
diff --git a/.github/workflows/continuous-integration-tfsec.yml b/.github/workflows/continuous-integration-tfsec.yml
new file mode 100644
index 000000000..2c9237304
--- /dev/null
+++ b/.github/workflows/continuous-integration-tfsec.yml
@@ -0,0 +1,14 @@
+name: Continuous integration
+on:
+ pull_request:
+jobs:
+ tfsec-pr-commenter:
+ name: tfsec PR commenter
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone repo
+ uses: actions/checkout@v3
+ - name: tfsec
+ uses: aquasecurity/tfsec-pr-commenter-action@v1.2.0
+ with:
+ github_token: ${{ github.token }}
diff --git a/.gitignore b/.gitignore
index 32b5134ca..a48d585a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -372,3 +372,15 @@ FodyWeavers.xsd
.env.*.local
!.env.development.local.example
!.env.database.example
+
+# Homebrew
+Brewfile.lock.json
+
+### Terraform
+.terraformrc*
+terraform.rc*
+*.tfstate*
+*.tfvars*
+!terraform.tfvars.example
+.terraform/
+backend.vars
diff --git a/Dockerfile b/Dockerfile
index 857c33aff..09afdf1a9 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,5 @@
# Stage 1
+ARG ASPNET_IMAGE_TAG=6.0.9-bullseye-slim
FROM mcr.microsoft.com/dotnet/sdk:6.0 as build
WORKDIR /build
@@ -27,7 +28,8 @@ RUN dotnet publish Dfe.Academies.Academisation.WebApi -c Release -o /app
COPY ./script/webapi-docker-entrypoint.sh /app/docker-entrypoint.sh
-FROM mcr.microsoft.com/dotnet/aspnet:6.0.9-bullseye-slim AS final
+ARG ASPNET_IMAGE_TAG
+FROM "mcr.microsoft.com/dotnet/aspnet:${ASPNET_IMAGE_TAG}" AS final
RUN apt-get update
RUN apt-get install unixodbc curl gnupg -y
diff --git a/terraform/.terraform-docs.yml b/terraform/.terraform-docs.yml
new file mode 100644
index 000000000..a6917808f
--- /dev/null
+++ b/terraform/.terraform-docs.yml
@@ -0,0 +1,26 @@
+---
+formatter: "markdown table"
+version: "~> 0.16"
+settings:
+ anchor: true
+ default: true
+ description: false
+ escape: true
+ hide-empty: false
+ html: true
+ indent: 2
+ lockfile: true
+ read-comments: true
+ required: true
+ sensitive: true
+ type: true
+sort:
+ enabled: true
+ by: name
+output:
+ file: README.md
+ mode: inject
+ template: |-
+
+ {{ .Content }}
+
diff --git a/terraform/.terraform-version b/terraform/.terraform-version
new file mode 100644
index 000000000..9d4f8239d
--- /dev/null
+++ b/terraform/.terraform-version
@@ -0,0 +1 @@
+1.2.9
diff --git a/terraform/.terraform.lock.hcl b/terraform/.terraform.lock.hcl
new file mode 100644
index 000000000..c0c526814
--- /dev/null
+++ b/terraform/.terraform.lock.hcl
@@ -0,0 +1,42 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/azure/azapi" {
+ version = "1.0.0"
+ constraints = ">= 0.5.0, >= 1.0.0"
+ hashes = [
+ "h1:UEarnWmn8kdWSjcPdFj/bl+wxxpjwxRhM0FjK4/IyIE=",
+ "zh:01a33aaefe4d185e70d926103eeb0ac9fefeadf750f69c5977ead2ae02e0b038",
+ "zh:1ce767851be07e432b4cdde91b40beef84f030432bb7b431ffda85b89305414d",
+ "zh:1cf15bc8430377091c06373c74a68ce61a9f36dd1455929a64e8083332f2c291",
+ "zh:4372f59b2761b3ae4b59d59f978af547cd8fae44d2b2e5baa91735b0ea3b16e2",
+ "zh:6602e2aae7937456418f53372d7139d2f56aea5e46dfd46634f9b202988178c0",
+ "zh:6f0945ee6ae05cbd708c10ee7b0f8c987032e35122a01d661188538f7548e59f",
+ "zh:6fc5e5017b8f87aff48732cc619f1295175913e3c1c039a170e8f0100a8233a2",
+ "zh:740f6c339f28406988204af6fadc9e58c754a22f234902b34c1f6d54421476c2",
+ "zh:7f003da3b64cb5129627b96a5eb0a03113853a0b17fd4cb77bd505fd27a8ca0b",
+ "zh:a1ed7aa209cdee91b013691ddb61d77eb3d840f9cba2f4c8b923ba80823c5912",
+ "zh:d6dad27af147a127027a8aa08a259f6dc418b09f842620e56e5db85547b1b090",
+ "zh:e67ddb150ff40cf9453fd56f47c2ac657ede1c1861b4d2f9009e98bddfc345b2",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/azurerm" {
+ version = "3.25.0"
+ constraints = ">= 3.20.0, >= 3.25.0"
+ hashes = [
+ "h1:y0PfEHDqCTSkv3bQpwM+RPEDB8+76PqwuRLF0lB7vhw=",
+ "zh:35ae77914a6280592a199a56c75a5f953cb5553f4416e50f4d4f47732eeb8d67",
+ "zh:3e2562a26b047e314a9fd28ee16ab460ce412fe00ed17583f3abfcc9c6998e0f",
+ "zh:431bc3cac313ee1e8f053cdf85f22d497a41d20497af83dfe531c9f5ecdee290",
+ "zh:48aa1304cbd9e7124ad3bf68be8260f2d38ca41d98f893d9e2652c2ed5c88e3d",
+ "zh:66eb8e90443aef5fc2bf96d12d70129cd961b4a93c2571c54b50f8aace5bf83f",
+ "zh:947d7301de9adbf669c64c341e2832b20da7712396b82b2abb71e34b231265bd",
+ "zh:afba5c2d0138946dcf71a43983b0d36383efef437edc594fd18d14348646b1fa",
+ "zh:b6296deeb3c119edc457b9ccaff3725d523702b9be4d7e4e15359e7ba4b37a3d",
+ "zh:e04d05092a42e1101fab92161403bc4a4baefcfd5325eacf881c0e06b29ef69b",
+ "zh:ed5d72359a36c712b6e1c58da00210c1becb2b23b33cf112545476020976f259",
+ "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
+ "zh:fbb17dc9e6213392873e9a6f32d90376a8d1a019e5db315b8a40c12fc01cec18",
+ ]
+}
diff --git a/terraform/Brewfile b/terraform/Brewfile
new file mode 100644
index 000000000..e862e7336
--- /dev/null
+++ b/terraform/Brewfile
@@ -0,0 +1,4 @@
+brew "tfenv"
+brew "terraform-docs"
+brew "tfsec"
+brew "az"
diff --git a/terraform/README.md b/terraform/README.md
new file mode 100644
index 000000000..fcad51b71
--- /dev/null
+++ b/terraform/README.md
@@ -0,0 +1,170 @@
+This documentation covers the deployment of the infrastructure to host the app.
+
+## Azure infrastructure
+
+The infrastructure is managed using [Terraform](https://www.terraform.io/).
+The state is stored remotely in encrypted Azure storage.
+[Terraform workspaces](https://www.terraform.io/docs/state/workspaces.html) are used to separate environments.
+
+#### Configuring the storage backend
+
+The Terraform state is stored remotely in Azure, this allows multiple team members to
+make changes and means the state file is backed up. The state file contains
+sensitive information so access to it should be restricted, and it should be stored
+encrypted at rest.
+
+##### Create a new storage backend
+
+This step only needs to be done once per project (eg. not per environment).
+If it has already been created, obtain the storage backend attributes and skip to the next step.
+
+The [Azure tutorial](https://docs.microsoft.com/en-us/azure/developer/terraform/store-state-in-azure-storage) outlines the steps to create a storage account and container for the state file. You will need:
+
+- resource_group_name: The name of the resource group used for the Azure Storage account.
+- storage_account_name: The name of the Azure Storage account.
+- container_name: The name of the blob container.
+- key: The name of the state store file to be created.
+
+##### Create a backend configuration file
+
+Create a new file named `backend.vars` with the following content:
+
+```
+resource_group_name = [the name of the Azure resource group]
+storage_account_name = [the name of the Azure Storage account]
+container_name = [the name of the blob container]
+key = "terraform.tstate"
+```
+
+##### Install dependencies
+
+We can use [Homebrew](https://brew.sh) to install the dependecies we need to deploy the infrastructure (eg. tfenv, Azure cli).
+These are listed in the `Brewfile`
+
+to install, run:
+
+```
+$ brew bundle
+```
+
+##### Log into azure with the Azure CLI
+
+Log in to your account:
+
+```
+$ az login
+```
+
+Confirm which account you are currently using:
+
+```
+$ az account show
+```
+
+To list the available subscriptions, run:
+
+```
+$ az account list
+```
+
+Then if needed, switch to it using the 'id':
+
+```
+$ az account set --subscription
+```
+
+##### Initialise Terraform
+
+Install the required terraform version with the Terraform version manager `tfenv`:
+
+```
+$ tfenv install
+```
+
+Initialize Terraform to download the required Terraform modules and configure the remote state backend
+to use the settings you specified in the previous step.
+
+`$ terraform init -backend-config=backend.vars`
+
+##### Create a Terraform variables file
+
+Each environment will need it's own `tfvars` file.
+
+Copy the `terraform.tfvars.example` to `environment-name.tfvars` and modify the contents as required
+
+##### Create the infrastructure
+
+Now Terraform has been initialised you can create a workspace if needed:
+
+`$ terraform workspace new staging`
+
+Or to check what workspaces already exist:
+
+`$ terraform workspace list`
+
+Switch to the new or existing workspace:
+
+`$ terraform workspace select staging`
+
+Plan the changes:
+
+`$ terraform plan -var-file=staging.tfvars`
+
+Terraform will ask you to provide any variables not specified in an `*.auto.tfvars` file.
+Now you can run:
+
+`$ terraform apply -var-file=staging.tfvars`
+
+If everything looks good, answer `yes` and wait for the new infrastructure to be created.
+
+##### Azure resources
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.2.9 |
+| [azapi](#requirement\_azapi) | >= 1.0.0 |
+| [azurerm](#requirement\_azurerm) | >= 3.25.0 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [azurerm](#provider\_azurerm) | 3.25.0 |
+
+## Modules
+
+| Name | Source | Version |
+|------|--------|---------|
+| [azure\_container\_apps\_hosting](#module\_azure\_container\_apps\_hosting) | github.com/DFE-Digital/terraform-azurerm-container-apps-hosting | 0.5.2 |
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [azurerm_key_vault.tfvars](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault) | resource |
+| [azurerm_key_vault_secret.tfvars](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_secret) | resource |
+| [azurerm_client_config.current](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/client_config) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [azure\_location](#input\_azure\_location) | Azure location in which to launch resources. | `string` | n/a | yes |
+| [container\_command](#input\_container\_command) | Container command | `list(any)` | n/a | yes |
+| [container\_secret\_environment\_variables](#input\_container\_secret\_environment\_variables) | Container secret environment variables | `map(string)` | n/a | yes |
+| [enable\_container\_registry](#input\_enable\_container\_registry) | Set to true to create a container registry | `bool` | n/a | yes |
+| [enable\_mssql\_database](#input\_enable\_mssql\_database) | Set to true to create an Azure SQL server/database, with aprivate endpoint within the virtual network | `bool` | n/a | yes |
+| [environment](#input\_environment) | Environment name. Will be used along with `project_name` as a prefix for all resources. | `string` | n/a | yes |
+| [image\_name](#input\_image\_name) | Image name | `string` | n/a | yes |
+| [project\_name](#input\_project\_name) | Project name. Will be used along with `environment` as a prefix for all resources. | `string` | n/a | yes |
+| [tags](#input\_tags) | Tags to be applied to all resources | `map(string)` | n/a | yes |
+| [tfvars\_filename](#input\_tfvars\_filename) | tfvars filename. This ensures that tfvars are kept up to date in Key Vault. | `string` | n/a | yes |
+| [virtual\_network\_address\_space](#input\_virtual\_network\_address\_space) | Virtual network address space CIDR | `string` | n/a | yes |
+
+## Outputs
+
+No outputs.
+
diff --git a/terraform/backend.tf b/terraform/backend.tf
new file mode 100644
index 000000000..6602f2060
--- /dev/null
+++ b/terraform/backend.tf
@@ -0,0 +1,3 @@
+terraform {
+ backend "azurerm" {}
+}
diff --git a/terraform/backend.vars.example b/terraform/backend.vars.example
new file mode 100644
index 000000000..589eb974c
--- /dev/null
+++ b/terraform/backend.vars.example
@@ -0,0 +1,4 @@
+resource_group_name = ""
+storage_account_name = ""
+container_name = ""
+key = "terraform.tstate"
diff --git a/terraform/container-apps-hosting.tf b/terraform/container-apps-hosting.tf
new file mode 100644
index 000000000..84bd85eb3
--- /dev/null
+++ b/terraform/container-apps-hosting.tf
@@ -0,0 +1,18 @@
+module "azure_container_apps_hosting" {
+ source = "github.com/DFE-Digital/terraform-azurerm-container-apps-hosting?ref=0.5.2"
+
+ environment = local.environment
+ project_name = local.project_name
+ azure_location = local.azure_location
+ tags = local.tags
+
+ virtual_network_address_space = local.virtual_network_address_space
+
+ enable_container_registry = local.enable_container_registry
+
+ image_name = local.image_name
+ container_command = local.container_command
+ container_secret_environment_variables = local.container_secret_environment_variables
+
+ enable_mssql_database = local.enable_mssql_database
+}
diff --git a/terraform/data.tf b/terraform/data.tf
new file mode 100644
index 000000000..cee07df25
--- /dev/null
+++ b/terraform/data.tf
@@ -0,0 +1 @@
+data "azurerm_client_config" "current" {}
diff --git a/terraform/key-vault-tfvars-secrets.tf b/terraform/key-vault-tfvars-secrets.tf
new file mode 100644
index 000000000..36da6624b
--- /dev/null
+++ b/terraform/key-vault-tfvars-secrets.tf
@@ -0,0 +1,48 @@
+resource "azurerm_key_vault" "tfvars" {
+ name = "${local.environment}${local.project_name}-tfvars"
+ location = module.azure_container_apps_hosting.azurerm_resource_group_default.location
+ resource_group_name = module.azure_container_apps_hosting.azurerm_resource_group_default.name
+ tenant_id = data.azurerm_client_config.current.tenant_id
+ sku_name = "standard"
+ soft_delete_retention_days = 7
+
+ access_policy {
+ tenant_id = data.azurerm_client_config.current.tenant_id
+ object_id = data.azurerm_client_config.current.object_id
+
+ key_permissions = [
+ "Create",
+ "Get",
+ ]
+
+ secret_permissions = [
+ "Set",
+ "Get",
+ "Delete",
+ "Purge",
+ "Recover"
+ ]
+ }
+
+ # It won't be possible to add/manage a network acl for this
+ # vault, as it will need to be accessable for multiple people.
+ # tfsec:ignore:azure-keyvault-specify-network-acl
+ network_acls {
+ bypass = "None"
+ default_action = "Allow"
+ }
+
+ purge_protection_enabled = true
+
+ tags = local.tags
+}
+
+# Expiry doesn't need to be set, as this is just used as a way to
+# store and share the tfvars
+# tfsec:ignore:azure-keyvault-ensure-secret-expiry
+resource "azurerm_key_vault_secret" "tfvars" {
+ name = "${local.environment}${local.project_name}-tfvars"
+ value = base64encode(file(local.tfvars_filename))
+ key_vault_id = azurerm_key_vault.tfvars.id
+ content_type = "text/plain+base64"
+}
diff --git a/terraform/locals.tf b/terraform/locals.tf
new file mode 100644
index 000000000..93f4ecd29
--- /dev/null
+++ b/terraform/locals.tf
@@ -0,0 +1,13 @@
+locals {
+ environment = var.environment
+ project_name = var.project_name
+ azure_location = var.azure_location
+ tags = var.tags
+ virtual_network_address_space = var.virtual_network_address_space
+ enable_container_registry = var.enable_container_registry
+ image_name = var.image_name
+ container_command = var.container_command
+ container_secret_environment_variables = var.container_secret_environment_variables
+ enable_mssql_database = var.enable_mssql_database
+ tfvars_filename = var.tfvars_filename
+}
diff --git a/terraform/providers.tf b/terraform/providers.tf
new file mode 100644
index 000000000..12bf2de93
--- /dev/null
+++ b/terraform/providers.tf
@@ -0,0 +1,6 @@
+provider "azurerm" {
+ features {}
+ skip_provider_registration = true
+}
+
+provider "azapi" {}
diff --git a/terraform/terraform.tfvars.example b/terraform/terraform.tfvars.example
new file mode 100644
index 000000000..4762044a5
--- /dev/null
+++ b/terraform/terraform.tfvars.example
@@ -0,0 +1,12 @@
+environment = "development"
+project_name = "myproject"
+azure_location = "uksouth"
+enable_container_registry = true
+image_name = "myimage"
+enable_mssql_database = true
+mssql_server_admin_password = "S3crEt"
+mssql_database_name = "mydatabase"
+container_command = ["/bin/bash", "-c", "echo hello && sleep 86400"]
+container_environment_variables = {
+ "ASPNETCORE_ENVIRONMENT" = "production"
+}
diff --git a/terraform/variables.tf b/terraform/variables.tf
new file mode 100644
index 000000000..f5a21875a
--- /dev/null
+++ b/terraform/variables.tf
@@ -0,0 +1,55 @@
+variable "environment" {
+ description = "Environment name. Will be used along with `project_name` as a prefix for all resources."
+ type = string
+}
+
+variable "tfvars_filename" {
+ description = "tfvars filename. This ensures that tfvars are kept up to date in Key Vault."
+ type = string
+}
+
+variable "project_name" {
+ description = "Project name. Will be used along with `environment` as a prefix for all resources."
+ type = string
+}
+
+variable "azure_location" {
+ description = "Azure location in which to launch resources."
+ type = string
+}
+
+variable "tags" {
+ description = "Tags to be applied to all resources"
+ type = map(string)
+}
+
+variable "virtual_network_address_space" {
+ description = "Virtual network address space CIDR"
+ type = string
+}
+
+variable "enable_container_registry" {
+ description = "Set to true to create a container registry"
+ type = bool
+}
+
+variable "image_name" {
+ description = "Image name"
+ type = string
+}
+
+variable "container_command" {
+ description = "Container command"
+ type = list(any)
+}
+
+variable "container_secret_environment_variables" {
+ description = "Container secret environment variables"
+ type = map(string)
+ sensitive = true
+}
+
+variable "enable_mssql_database" {
+ description = "Set to true to create an Azure SQL server/database, with aprivate endpoint within the virtual network"
+ type = bool
+}
diff --git a/terraform/versions.tf b/terraform/versions.tf
new file mode 100644
index 000000000..516868c8e
--- /dev/null
+++ b/terraform/versions.tf
@@ -0,0 +1,13 @@
+terraform {
+ required_version = ">= 1.2.9"
+ required_providers {
+ azurerm = {
+ source = "hashicorp/azurerm"
+ version = ">= 3.25.0"
+ }
+ azapi = {
+ source = "Azure/azapi"
+ version = ">= 1.0.0"
+ }
+ }
+}