Skip to content

Commit

Permalink
Merge pull request #12 from fluxcd/azure-gh-actions
Browse files Browse the repository at this point in the history
Add azure github actions tf module
  • Loading branch information
darkowlzz authored Jul 20, 2023
2 parents dab9830 + 61d0804 commit 9baf9e8
Show file tree
Hide file tree
Showing 4 changed files with 290 additions and 0 deletions.
97 changes: 97 additions & 0 deletions tf-modules/azure/github-actions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Azure GitHub Actions Secrets and Variables

Configuration in this directory registers an Azure app to be used in CI,
generates a client secret for the app, creates a custom role with the requested
permissions and assigns the role to the Azure app service account. The app
client details are then written to GitHub actions secrets and variables of a
given repository.

By default, the following GitHub actions secrets are created:
- `ARM_CLIENT_ID`
- `ARM_CLIENT_SECRET`
- `ARM_SUBSCRIPTION_ID`
- `ARM_TENANT_ID`

and `TF_VAR_azure_location` actions variable is created. All of these names
are overridable, see `variables.tf`.

It also supports adding custom secrets and variables in addition to the above.

**NOTE:** Overwriting existing GitHub secrets and variables are not supported.

## Usage

```hcl
module "azure_gh_actions" {
source = "git::https://github.com/fluxcd/test-infra.git//tf-modules/azure/github-actions"
azure_owners = ["owner-id-1", "owner-id-2"]
azure_app_name = "test-app-1"
azure_app_description = "A test app."
azure_permissions = [
"Microsoft.Kubernetes/*",
"Microsoft.ContainerRegistry/*"
]
azure_location = "eastus"
github_project = "repo-name"
github_variable_custom = {
"SOME_VAR1" = "some-val1",
"SOME_var2" = "some-val2"
}
github_secret_custom = {
"SECRET1" = "some-secret1",
"SECRET2" = "some-secret2"
}
}
```

## Azure Requirements

AzureAD permissions:
- To use a Service Account, grant the following Microsoft Graph API permissions:
- `Application.ReadWrite.OwnedBy`
- `Directory.Read.All`
- To use a User account, ensure that the user has one of the following
directory roles:
- `Application Administrator`
- `Global Administrator`

Refer [azuread_application docs](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application)
and [azuread_service_principal docs](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/service_principal)
for more details.

AzureRM permissions:
- The following IAM permissions are required when using a Service Account:
- `Microsoft.Resources/subscriptions/read`
- `Microsoft.Authorization/roleDefinitions/write`
- `Microsoft.Authorization/roleDefinitions/delete`
- `Microsoft.Authorization/roleAssignments/read`
- `Microsoft.Authorization/roleAssignments/write`
- `Microsoft.Authorization/roleAssignments/delete`

## GitHub Requirements

Create a GitHub fine-grained token for the target repository with the following
repository permissions:
- `Read access to metadata`
- `Read and Write access to actions variables and secrets`

## Provider Configuration

Configure the AzureRM, AzureAD and Github providers with the following
environment variables:
```sh
export ARM_CLIENT_ID=""
export ARM_CLIENT_SECRET=""
export ARM_SUBSCRIPTION_ID=""
export ARM_TENANT_ID=""

export GITHUB_TOKEN=""
```

**NOTE:** When using Azure user account, logging in using the Azure CLI should
be enough to configure the Azure providers.

Check the respective provider docs for more details.
94 changes: 94 additions & 0 deletions tf-modules/azure/github-actions/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
provider "azurerm" {
skip_provider_registration = true
features {}
}

data "azurerm_subscription" "primary" {}
data "azuread_client_config" "current" {}

# Register application, create service principal for it and generate client
# secret.
resource "azuread_application" "app" {
display_name = var.azure_app_name
owners = concat(var.azure_owners, [data.azuread_client_config.current.object_id])
}

resource "azuread_service_principal" "app_sp" {
application_id = azuread_application.app.application_id
use_existing = true
}

resource "azuread_application_password" "app_secret" {
application_object_id = resource.azuread_application.app.id
display_name = var.azure_app_secret_name
}

# Define custom role.
resource "azurerm_role_definition" "role" {
name = var.azure_app_name
scope = data.azurerm_subscription.primary.id
description = var.azure_app_description

permissions {
actions = var.azure_permissions
}

assignable_scopes = [
data.azurerm_subscription.primary.id,
]
}

# Assign role to the registered app's service principal.
resource "azurerm_role_assignment" "assignment" {
scope = data.azurerm_subscription.primary.id
role_definition_id = azurerm_role_definition.role.role_definition_resource_id
principal_id = azuread_service_principal.app_sp.object_id
}

# Add client details in github repo.
resource "github_actions_secret" "client_id" {
repository = var.github_project
secret_name = var.github_secret_client_id_name
plaintext_value = azuread_application.app.application_id
}

resource "github_actions_secret" "client_secret" {
repository = var.github_project
secret_name = var.github_secret_client_secret_name
plaintext_value = azuread_application_password.app_secret.value
}

resource "github_actions_secret" "subscription_id" {
repository = var.github_project
secret_name = var.github_secret_subscription_id_name
plaintext_value = data.azurerm_subscription.primary.subscription_id
}

resource "github_actions_secret" "tenant_id" {
repository = var.github_project
secret_name = var.github_secret_tenant_id_name
plaintext_value = azuread_service_principal.app_sp.application_tenant_id
}

resource "github_actions_variable" "location" {
repository = var.github_project
variable_name = var.github_variable_location_name
value = var.azure_location
}

resource "github_actions_variable" "custom" {
for_each = var.github_variable_custom

repository = var.github_project
variable_name = each.key
value = each.value
}

resource "github_actions_secret" "custom" {
# Mark only the key as nonsensitive.
for_each = nonsensitive(toset(keys(var.github_secret_custom)))

repository = var.github_project
secret_name = each.key
plaintext_value = var.github_secret_custom[each.key]
}
81 changes: 81 additions & 0 deletions tf-modules/azure/github-actions/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
variable "azure_owners" {
description = "Object IDs of the Azure resource owners"
type = list(string)
default = []
}

variable "github_project" {
description = "Name of the GitHub project to add secrets to"
type = string
}

variable "azure_app_name" {
description = "Name of the Azure app to register"
type = string
}

variable "azure_app_description" {
description = "Description of the Azure app, will be used to provide context to the role assigned to the app"
type = string
}

variable "azure_app_secret_name" {
description = "Name of the Azure app secret"
type = string
default = "tf-generated-secret"
}

variable "azure_permissions" {
description = "List of permissions to grant to the created app"
type = list(string)
default = []
}

variable "github_secret_client_id_name" {
description = "GitHub secret name for Azure app client ID"
type = string
default = "ARM_CLIENT_ID"
}

variable "github_secret_client_secret_name" {
description = "GitHub secret name for Azure app client secret"
type = string
default = "ARM_CLIENT_SECRET"
}

variable "github_secret_subscription_id_name" {
description = "GitHub secret name for Azure subscription ID"
type = string
default = "ARM_SUBSCRIPTION_ID"
}

variable "github_secret_tenant_id_name" {
description = "GitHub secret name for Azure tenant ID"
type = string
default = "ARM_TENANT_ID"
}

variable "github_variable_location_name" {
description = "GitHub variable name for Azure location"
type = string
default = "TF_VAR_azure_location"
}

variable "azure_location" {
description = "Azure location used by the tests"
type = string
default = "eastus"
}

variable "github_variable_custom" {
description = "A map of custom GitHub variables to be created"
type = map(string)
default = {}
}

variable "github_secret_custom" {
description = "A map of custom GitHub secrets to be created"
type = map(string)
default = {}
sensitive = true
}
18 changes: 18 additions & 0 deletions tf-modules/azure/github-actions/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 3.62.1"
}

azuread = {
source = "hashicorp/azuread"
version = ">= 2.39.0"
}

github = {
source = "integrations/github"
version = ">= 5.28.1"
}
}
}

0 comments on commit 9baf9e8

Please sign in to comment.