diff --git a/docs/guides/authentication.md b/docs/guides/authentication.md index afa498f4..d2b37175 100644 --- a/docs/guides/authentication.md +++ b/docs/guides/authentication.md @@ -4,11 +4,11 @@ page_title: Authentication # Authentication -Environment variables are the only supported authentication method. They should be present when running any of the `terraform` commands. For example: +Environment variables are the only supported authentication method, and should be present when running any `terraform` command. For example: ```bash -$ export GOOGLE_APPLICATION_CREDENTIALS_DATA="$(cat service_account.json)" -$ terraform apply +export GOOGLE_APPLICATION_CREDENTIALS_DATA="$(cat service_account.json)" +terraform apply ``` ## Amazon Web Services @@ -17,7 +17,15 @@ $ terraform apply - `AWS_SECRET_ACCESS_KEY` - Secret access key. - `AWS_SESSION_TOKEN` - (Optional) Session token. -See the [AWS documentation](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html) for more information. +See the [AWS documentation](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html) to obtain these variables directly. + +Alternatively, for more idiomatic or advanced use cases, follow the [Terraform AWS provider documentation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration) and run the following commands in the [`permissions/aws`](https://github.com/iterative/terraform-provider-iterative/tree/master/docs/guides/permissions/aws) directory: + +```bash +terraform init && terraform apply +export AWS_ACCESS_KEY_ID="$(terraform output --raw aws_access_key_id)" +export AWS_SECRET_ACCESS_KEY="$(terraform output --raw aws_secret_access_key)" +``` ## Microsoft Azure @@ -26,13 +34,30 @@ See the [AWS documentation](https://docs.aws.amazon.com/cli/latest/userguide/cli - `AZURE_SUBSCRIPTION_ID` - Subscription identifier. - `AZURE_TENANT_ID` - Tenant identifier. -See the [Azure documentation](https://docs.microsoft.com/en-us/python/api/azure-identity/azure.identity.environmentcredential) for more information. +See the [Azure documentation](https://docs.microsoft.com/en-us/python/api/azure-identity/azure.identity.environmentcredential) to obtain these variables directly. + +Alternatively, for more idiomatic or advanced use cases, follow the [Terraform Azure provider documentation](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/azure_cli) and run the following commands in the [`permissions/az`](https://github.com/iterative/terraform-provider-iterative/tree/master/docs/guides/permissions/az) directory: + +```bash +terraform init && terraform apply +export AZURE_TENANT_ID="$(terraform output --raw azure_tenant_id)" +export AZURE_SUBSCRIPTION_ID="$(terraform output --raw azure_subscription_id)" +export AZURE_CLIENT_ID="$(terraform output --raw azure_client_id)" +export AZURE_CLIENT_SECRET="$(terraform output --raw azure_client_secret)" +``` ## Google Cloud Platform - `GOOGLE_APPLICATION_CREDENTIALS` - Path to (or contents of) a service account JSON key file. -See the [GCP documentation](https://cloud.google.com/docs/authentication/getting-started#creating_a_service_account) for more information. +See the [GCP documentation](https://cloud.google.com/docs/authentication/getting-started#creating_a_service_account) to obtain these variables directly. + +Alternatively, for more idiomatic or advanced use cases, follow the [Terraform GCP provider documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/getting_started) and run the following commands in the [`permissions/gcp`](https://github.com/iterative/terraform-provider-iterative/tree/master/docs/guides/permissions/gcp) directory: + +```bash +terraform init && terraform apply +export GOOGLE_APPLICATION_CREDENTIALS_DATA="$(terraform output --raw google_application_credentials_data)" +``` ## Kubernetes @@ -40,3 +65,10 @@ Either one of: - `KUBECONFIG` - Path to a [`kubeconfig` file](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/#the-kubeconfig-environment-variable). - `KUBECONFIG_DATA` - Alternatively, the **contents** of a `kubeconfig` file. + +Alternatively, authenticate with a local `kubeconfig` file and run the following commands in the [`permissions/k8s`](https://github.com/iterative/terraform-provider-iterative/tree/master/docs/guides/permissions/k8s) directory: + +```bash +kubectl apply --filename main.yml +export KUBECONFIG_DATA="$(bash kubeconfig.sh)" +``` diff --git a/docs/guides/permissions/aws/main.tf b/docs/guides/permissions/aws/main.tf new file mode 100644 index 00000000..2c2d9c0b --- /dev/null +++ b/docs/guides/permissions/aws/main.tf @@ -0,0 +1,81 @@ +terraform { + required_providers { + aws = { source = "hashicorp/aws", version = "~> 4.3.0" } + } +} + +variable "aws_region" { + description = "Name of the Amazon Web Services region to use" +} + +provider "aws" { + region = var.aws_region +} + +resource "aws_iam_user" "task" { + name = "task" +} +resource "aws_iam_access_key" "task" { + user = aws_iam_user.task.name +} +resource "aws_iam_user_policy" "task" { + name = aws_iam_user.task.name + user = aws_iam_user.task.name + policy = data.aws_iam_policy_document.task.json +} + +data "aws_iam_policy_document" "task" { + statement { + actions = [ + "autoscaling:CreateAutoScalingGroup", + "autoscaling:DeleteAutoScalingGroup", + "autoscaling:DescribeAutoScalingGroups", + "autoscaling:DescribeScalingActivities", + "autoscaling:UpdateAutoScalingGroup", + "ec2:AuthorizeSecurityGroupEgress", + "ec2:AuthorizeSecurityGroupIngress", + "ec2:CancelSpotInstanceRequests", + "ec2:CreateKeyPair", + "ec2:CreateLaunchTemplate", + "ec2:CreateSecurityGroup", + "ec2:CreateTags", + "ec2:DeleteKeyPair", + "ec2:DeleteLaunchTemplate", + "ec2:DeleteSecurityGroup", + "ec2:DescribeAutoScalingGroups", + "ec2:DescribeImages", + "ec2:DescribeInstances", + "ec2:DescribeKeyPairs", + "ec2:DescribeLaunchTemplates", + "ec2:DescribeScalingActivities", + "ec2:DescribeSecurityGroups", + "ec2:DescribeSpotInstanceRequests", + "ec2:DescribeSubnets", + "ec2:DescribeVpcs", + "ec2:GetLaunchTemplateData", + "ec2:ImportKeyPair", + "ec2:ModifyImageAttribute", + "ec2:ModifyLaunchTemplate", + "ec2:RequestSpotInstances", + "ec2:RevokeSecurityGroupEgress", + "ec2:RevokeSecurityGroupIngress", + "ec2:RunInstances", + "ec2:TerminateInstances", + "s3:CreateBucket", + "s3:DeleteBucket", + "s3:DeleteObject", + "s3:GetObject", + "s3:ListBucket", + "s3:PutObject", + ] + resources = ["*"] + } +} + +output "aws_access_key_id" { + value = aws_iam_access_key.task.id +} +output "aws_secret_access_key" { + value = aws_iam_access_key.task.secret + sensitive = true +} diff --git a/docs/guides/permissions/az/main.tf b/docs/guides/permissions/az/main.tf new file mode 100644 index 00000000..9d1fd4df --- /dev/null +++ b/docs/guides/permissions/az/main.tf @@ -0,0 +1,104 @@ +terraform { + required_providers { + azuread = { source = "hashicorp/azuread", version = "~> 2.18.0" } + azurerm = { source = "hashicorp/azurerm", version = "~> 2.98.0" } + } +} + +provider "azuread" {} +provider "azurerm" { + features {} +} + +data "azuread_client_config" "current" {} +data "azurerm_subscription" "current" {} + +resource "azuread_application" "task" { + display_name = "task" + owners = [data.azuread_client_config.current.object_id] +} +resource "azuread_application_password" "task" { + application_object_id = azuread_application.task.object_id +} +resource "azuread_service_principal" "task" { + application_id = azuread_application.task.application_id + app_role_assignment_required = false + owners = [data.azuread_client_config.current.object_id] +} +resource "azurerm_role_definition" "task" { + name = azuread_application.task.display_name + scope = data.azurerm_subscription.current.id + permissions { + actions = [ + "Microsoft.Compute/virtualMachineScaleSets/delete", + "Microsoft.Compute/virtualMachineScaleSets/delete/action", + "Microsoft.Compute/virtualMachineScaleSets/instanceView/read", + "Microsoft.Compute/virtualMachineScaleSets/networkInterfaces/read", + "Microsoft.Compute/virtualMachineScaleSets/publicIPAddresses/read", + "Microsoft.Compute/virtualMachineScaleSets/read", + "Microsoft.Compute/virtualMachineScaleSets/scale/action", + "Microsoft.Compute/virtualMachineScaleSets/skus/read", + "Microsoft.Compute/virtualMachineScaleSets/start/action", + "Microsoft.Compute/virtualMachineScaleSets/virtualMachines/delete", + "Microsoft.Compute/virtualMachineScaleSets/virtualMachines/networkInterfaces/ipConfigurations/publicIPAddresses/read", + "Microsoft.Compute/virtualMachineScaleSets/virtualMachines/networkInterfaces/ipConfigurations/read", + "Microsoft.Compute/virtualMachineScaleSets/virtualMachines/networkInterfaces/read", + "Microsoft.Compute/virtualMachineScaleSets/virtualMachines/read", + "Microsoft.Compute/virtualMachineScaleSets/virtualMachines/write", + "Microsoft.Compute/virtualMachineScaleSets/vmSizes/read", + "Microsoft.Compute/virtualMachineScaleSets/write", + "Microsoft.Compute/virtualMachines/delete", + "Microsoft.Compute/virtualMachines/read", + "Microsoft.Compute/virtualMachines/write", + "Microsoft.Network/networkInterfaces/delete", + "Microsoft.Network/networkInterfaces/join/action", + "Microsoft.Network/networkInterfaces/read", + "Microsoft.Network/networkInterfaces/write", + "Microsoft.Network/networkSecurityGroups/delete", + "Microsoft.Network/networkSecurityGroups/join/action", + "Microsoft.Network/networkSecurityGroups/read", + "Microsoft.Network/networkSecurityGroups/write", + "Microsoft.Network/publicIPAddresses/delete", + "Microsoft.Network/publicIPAddresses/join/action", + "Microsoft.Network/publicIPAddresses/read", + "Microsoft.Network/publicIPAddresses/write", + "Microsoft.Network/virtualNetworks/delete", + "Microsoft.Network/virtualNetworks/read", + "Microsoft.Network/virtualNetworks/subnets/delete", + "Microsoft.Network/virtualNetworks/subnets/join/action", + "Microsoft.Network/virtualNetworks/subnets/read", + "Microsoft.Network/virtualNetworks/subnets/write", + "Microsoft.Network/virtualNetworks/write", + "Microsoft.Resources/subscriptions/resourceGroups/delete", + "Microsoft.Resources/subscriptions/resourceGroups/read", + "Microsoft.Resources/subscriptions/resourceGroups/write", + "Microsoft.Storage/storageAccounts/blobServices/containers/delete", + "Microsoft.Storage/storageAccounts/blobServices/containers/read", + "Microsoft.Storage/storageAccounts/blobServices/containers/write", + "Microsoft.Storage/storageAccounts/delete", + "Microsoft.Storage/storageAccounts/listKeys/action", + "Microsoft.Storage/storageAccounts/read", + "Microsoft.Storage/storageAccounts/write", + ] + } +} +resource "azurerm_role_assignment" "task" { + name = azurerm_role_definition.task.name + principal_id = azuread_service_principal.task.object_id + role_definition_id = azurerm_role_definition.task.role_definition_resource_id + scope = data.azurerm_subscription.current.id +} + +output "azure_subscription_id" { + value = basename(data.azurerm_subscription.current.id) +} +output "azure_tenant_id" { + value = data.azurerm_subscription.current.tenant_id +} +output "azure_client_id" { + value = azuread_application.task.application_id +} +output "azure_client_secret" { + value = azuread_application_password.task.value + sensitive = true +} diff --git a/docs/guides/permissions/gcp/main.tf b/docs/guides/permissions/gcp/main.tf new file mode 100644 index 00000000..1a42d3af --- /dev/null +++ b/docs/guides/permissions/gcp/main.tf @@ -0,0 +1,81 @@ +terraform { + required_providers { + google = { source = "hashicorp/google", version = "~> 4.12.0" } + } +} + +variable "gcp_project" { + description = "Name of the Google Cloud project to use" +} + +provider "google" { + project = var.gcp_project +} + +data "google_project" "current" {} + +resource "google_service_account" "task" { + account_id = "task-service-account" +} +resource "google_service_account_key" "task" { + service_account_id = google_service_account.task.email +} +resource "google_project_iam_binding" "task" { + project = data.google_project.current.project_id + role = "projects/${data.google_project.current.project_id}/roles/${google_project_iam_custom_role.task.role_id}" + members = ["serviceAccount:${google_service_account.task.email}"] +} +resource "google_project_iam_custom_role" "task" { + role_id = replace("${google_service_account.task.account_id}-role", "-", "_") + title = replace("${google_service_account.task.account_id}-role", "-", "_") + permissions = [ + "compute.acceleratorTypes.get", + "compute.diskTypes.get", + "compute.disks.create", + "compute.firewalls.create", + "compute.firewalls.delete", + "compute.firewalls.get", + "compute.globalOperations.get", + "compute.instanceGroupManagers.create", + "compute.instanceGroupManagers.delete", + "compute.instanceGroupManagers.get", + "compute.instanceGroupManagers.update", + "compute.instanceGroups.create", + "compute.instanceGroups.delete", + "compute.instanceGroups.get", + "compute.instanceTemplates.create", + "compute.instanceTemplates.delete", + "compute.instanceTemplates.get", + "compute.instanceTemplates.useReadOnly", + "compute.instances.create", + "compute.instances.delete", + "compute.instances.get", + "compute.instances.setMetadata", + "compute.instances.setServiceAccount", + "compute.instances.setTags", + "compute.machineTypes.get", + "compute.networks.get", + "compute.networks.updatePolicy", + "compute.subnetworks.use", + "compute.subnetworks.useExternalIp", + "compute.zoneOperations.get", + "iam.serviceAccounts.actAs", + "storage.buckets.create", + "storage.buckets.delete", + "storage.buckets.get", + "storage.multipartUploads.abort", + "storage.multipartUploads.create", + "storage.multipartUploads.list", + "storage.multipartUploads.listParts", + "storage.objects.create", + "storage.objects.delete", + "storage.objects.get", + "storage.objects.list", + "storage.objects.update", + ] +} + +output "google_application_credentials_data" { + value = base64decode(google_service_account_key.task.private_key) + sensitive = true +} diff --git a/docs/guides/permissions/k8s/kubeconfig.sh b/docs/guides/permissions/k8s/kubeconfig.sh new file mode 100644 index 00000000..0b7dbd5b --- /dev/null +++ b/docs/guides/permissions/k8s/kubeconfig.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -euxo pipefail +SERVER="$(kubectl config view --raw --flatten --output jsonpath='{.clusters[0].cluster.server}')" +AUTHORITY="$(kubectl config view --raw --flatten --output jsonpath='{.clusters[0].cluster.certificate-authority-data}')" +SECRET="$(kubectl get serviceaccount task --output jsonpath="{.secrets[0].name}")" +TOKEN="$(kubectl get secret "$SECRET" --output jsonpath="{.data.token}" | base64 --decode)" +export KUBECONFIG="$(mktemp)" +{ + kubectl config set-cluster cluster --server="https://$SERVER" + kubectl config set clusters.cluster.certificate-authority-data "$AUTHORITY" + kubectl config set-credentials task --token="$TOKEN" + kubectl config set-context cluster --cluster=cluster --user=task + kubectl config use-context cluster +} >/dev/null +cat "$KUBECONFIG" +rm "$KUBECONFIG" diff --git a/docs/guides/permissions/k8s/main.yml b/docs/guides/permissions/k8s/main.yml new file mode 100644 index 00000000..f4ee7324 --- /dev/null +++ b/docs/guides/permissions/k8s/main.yml @@ -0,0 +1,33 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: task +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: task +rules: + - apiGroups: + - "" + - apps + - batch + resources: + - configmaps + - events + - jobs + - persistentvolumeclaims + - pods + verbs: ["*"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: task +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: task +subjects: + - kind: ServiceAccount + name: task