Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add azure github actions tf module #12

Merged
merged 1 commit into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
}
}
}