### Install Upbound CLI Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Quickstart.md Download and install the Upbound `up` command-line tool. Verify the installation with `up --version`. ```shell curl -sL "https://cli.upbound.io" | sh mv up /usr/local/bin/ ``` ```shell $ up version v0.35.0 ``` -------------------------------- ### Install Upbound Universal Crossplane (UXP) Source: https://context7.com/upbound/provider-opentofu/llms.txt Installs the Up CLI and UXP. Verify UXP is running by checking the pods in the upbound-system namespace. ```shell # Install the Up CLI and UXP curl -sL "https://cli.upbound.io" | sh && mv up /usr/local/bin/ up uxp install # Verify UXP is running kubectl get pods -n upbound-system # NAME READY STATUS RESTARTS AGE # crossplane-649f76c8db-g29b2 1/1 Running 0 48s # crossplane-rbac-manager-645fdf89d6-t7mhh 1/1 Running 0 48s ``` -------------------------------- ### Install Universal Crossplane (UXP) Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Quickstart.md Install Upbound Universal Crossplane using the `up` command-line tool. Verify UXP pods are running with `kubectl get pods -n upbound-system`. ```shell $ up uxp install UXP 1.18.0-up.1 installed ``` ```shell $ kubectl get pods -n upbound-system NAME READY STATUS RESTARTS AGE crossplane-649f76c8db-g29b2 1/1 Running 0 48s crossplane-rbac-manager-645fdf89d6-t7mhh 1/1 Running 0 48s ``` -------------------------------- ### Install OpenTofu Provider Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Quickstart.md Install the OpenTofu provider into the Kubernetes cluster using a YAML configuration file. Apply the configuration with `kubectl apply -f`. Verify the installation with `kubectl get providers`. ```yaml apiVersion: pkg.crossplane.io/v1 kind: Provider metadata: name: provider-opentofu spec: package: xpkg.upbound.io/upbound/provider-opentofu: ``` ```shell $ kubectl get providers NAME INSTALLED HEALTHY PACKAGE AGE provider-opentofu True True xpkg.upbound.io/upbound/provider-opentofu:v0.1.0 15s ``` -------------------------------- ### Install Provider OpenTofu Source: https://context7.com/upbound/provider-opentofu/llms.txt Applies a Provider manifest to install the provider-opentofu package. Wait for the provider to become HEALTHY. ```yaml # provider-opentofu.yaml apiVersion: pkg.crossplane.io/v1 kind: Provider metadata: name: provider-opentofu spec: package: xpkg.upbound.io/upbound/provider-opentofu:v0.1.0 ``` ```shell kubectl apply -f provider-opentofu.yaml # Wait for HEALTHY=True (up to 5 minutes) kubectl get providers # NAME INSTALLED HEALTHY PACKAGE AGE # provider-opentofu True True xpkg.upbound.io/upbound/provider-opentofu:v0.1.0 15s ``` -------------------------------- ### GCP JSON Key File Example Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Quickstart.md This is an example of a GCP service account JSON key file. Save this content to a file named `gcp-credentials.json`. ```json { "type": "service_account", "project_id": "caramel-goat-354919", "private_key_id": "e97e40a4a27661f12345678f4bd92139324dbf46", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCwA+6MWRhmcPB3\nF/irb5MDPYAT6BWr7Vu/16U8FbCHk7xtsAWYjKXKHu5mGzum4F781sM0aMCeitlv\n+jr2y7Ny23S9uP5W2kfnD/lfj0EjCdfoaN3m7W0j4DrriJviV6ESeSdb0Ehg+iEW\ngNrkb/ljigYgsSLMuemby5lvJVINUazXJtGUEZew+iAOnI4/j/IrDXPCYVNo5z+b\neiMsDYWfccenWGOQf1hkbVWyKqzsInxu8NQef3tNhoUXNOn+/kgarOA5VTYvFUPr\n2l1P9TxzrcYuL8XK++HjVj5mcNaWXNN+jnFpxjMIJOiDJOZoAo0X7tuCJFXtAZbH\n9P61GjhbAgMBAAECggEARXo31kRw4jbgZFIdASa4hAXpoXHx4/x8Q9yOR4pUNR/2\nt+FMRCv4YTEWb01+nV9hfzISuYRDzBEIxS+jyLkda0/+48i69HOTAD0I9VRppLgE\ne97e40a4a27661f12345678f4bd92139324dbf46+2H7ulQDtbEgfcWpNMQcL2JiFq+WS\neh3H0gHSFFIWGnAM/xofrlhGsN64palZmbt2YiKXcHPT+WgLbD45mT5j9oMYxBJf\nPkUUX5QibSSBQyvNqCgRKHSnsY9yAkoNTbPnEV0clQ4FmSccogyS9uPEocQDefuY\nY7gpwSzjXpaw7tP5scK3NtWmmssi+dwDadfLrKF7oQKBgQDjIZ+jwAggCp7AYB/S\n6dznl5/G28Mw6CIM6kPgFnJ8P/C/Yi2y/OPKFKhMs2ecQI8lJfcvvpU/z+kZizcG\nr/7iRMR/SX8n1eqS8XfWKeBzIdwQmiKyRg2AKelGKljuVtI8sXKv9t6cm8RkWKuZ\n9uVroTCPWGpIrh2EMxLeOrlm0QKBgQDGYxoBvl5GfrOzjhYOa5GBgGYYPdE7kNny\nhpHE9CrPZFIcb5nGMlBCOfV+bqA9ALCXKFCr0eHhTjk9HjHfloxuxDmz34vC0xXG\ncegqfV9GNKZPDctysAlCWW/dMYw4+tzAgoG9Qm13Iyfi2Ikll7vfeMX7fH1cnJs0\nnYpN9LYPawKBgQCwMi09QoMLGDH+2pLVc0ZDAoSYJ3NMRUfk7Paqp784VAHW9bqt\n1zB+W3gTyDjgJdTl5IXVK+tsDUWu4yhUr8LylJY6iDF0HaZTR67HHMVZizLETk4M\nLfvbKKgmHkPO4NtG6gEmMESRCOVZUtAMKFPhIrIhAV2x9CBBpb1FWBjrgQKBgQCMg\nMTXt0ZBs0ScrG9vp5CGa18KC+S3oUOjK/qyACmCqhtd+hKHIxHx3/FQPBWb4rDJRsZHH7C6URR1pHzJmhCWgKGsvYrXkNxtiyPXwnU7PNP9JNuCWa45dr/vE/uxcbccK4JnWJ8+Kk/9LEX0mjtDm7wtLVlTswYhP6AP69RoMQ==\n-----END PRIVATE KEY----- ", "client_email": "my-sa-313@caramel-goat-354919.iam.gserviceaccount.com", "client_id": "103735491955093092925", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/my-sa-313%40caramel-goat-354919.iam.gserviceaccount.com" } ``` -------------------------------- ### Pin Module Versions Example Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/RemoteModulePullPolicy.md Demonstrates how to correctly pin module versions using Git references. Avoid floating references like `main` in production environments. ```yaml # Good - pinned version module: git::https://github.com/org/repo?ref=v1.0.0 # Avoid - floating ref module: git::https://github.com/org/repo?ref=main ``` -------------------------------- ### Create OpenTofu Workspace for GCP Storage Bucket Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Quickstart.md Create a `Workspace` resource to deploy a GCP storage bucket. This example uses an inline HCL source for simplicity. The `crossplane.io/external-name` annotation sets the bucket name. ```yaml apiVersion: opentofu.upbound.io/v1beta1 kind: Workspace metadata: name: example-inline annotations: # The opentofu workspace will be named 'coolbucket'. If you omit this # annotation it would be derived from metadata.name - e.g. 'example-inline'. crossplane.io/external-name: coolbucket spec: forProvider: # Workspaces default to using a remote source - like workspace-remote.yaml. # For simple cases you can use an inline source to specify the content of # main.tf as opaque, inline HCL. source: Inline module: | // Outputs are written to the connection secret. output "url" { value = google_storage_bucket.example.self_link } resource "random_id" "example" { byte_length = 4 } // The google provider and remote state are configured by the provider // config - see providerconfig.yaml. resource "google_storage_bucket" "example" { name = "crossplane-example-${opentofu.workspace}-${random_id.example.hex}" location = "US" force_destroy = true public_access_prevention = "enforced" } writeConnectionSecretToRef: namespace: default name: opentofu-workspace-example-inline ``` -------------------------------- ### Configure OpenTofu CLI with .tofurc Source: https://context7.com/upbound/provider-opentofu/llms.txt Mount a `.tofurc` file as a Secret to configure the OpenTofu CLI, for example, to set provider mirrors or network timeouts. The filename must be `.tofurc`. ```yaml apiVersion: opentofu.upbound.io/v1beta1 kind: ProviderConfig metadata: name: default spec: credentials: - filename: .tofurc # exact filename required source: Secret secretRef: namespace: upbound-system name: tofurc key: .tofurc configuration: | terraform { backend "kubernetes" { secret_suffix = "providerconfig-default" namespace = "upbound-system" in_cluster_config = true } } ``` -------------------------------- ### Calculate Reconciliation Speedup Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/RemoteModulePullPolicy.md Example calculation showing performance improvement in reconciliation speed by using `IfNotPresent`, reducing download time after the initial download. ```text Download time savings: - 50MB module at 100Mbps = 4 seconds download time - 6 reconciliations/hour × 4 seconds = 24 seconds/hour wasted - With IfNotPresent: 4 seconds once, then <1 second for subsequent reconciliations - Reconciliation speedup: 75%+ after first reconciliation ``` -------------------------------- ### Calculate Network Savings Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/RemoteModulePullPolicy.md Example calculation demonstrating significant network egress cost savings by using `IfNotPresent` compared to the default `Always` policy. ```text Scenario: - 100 workspaces - 50MB module size - 6 reconciliations per hour - $0.12/GB network egress (AWS example) Without IfNotPresent: - Per workspace: 50MB × 6 × 24 = 7.2GB/day - 100 workspaces: 720GB/day - Monthly cost: 720GB × 30 × $0.12 = $2,592/month With IfNotPresent: - Per workspace: 50MB/day (one download) - 100 workspaces: 5GB/day - Monthly cost: 5GB × 30 × $0.12 = $18/month Savings: $2,574/month (99.3% reduction) ``` -------------------------------- ### Define OpenTofu Workspace with Remote Source and Environment Variables Source: https://github.com/upbound/provider-opentofu/blob/main/README.md Configure an OpenTofu `Workspace` using a remote module source (e.g., a Git repository). This example demonstrates passing environment variables, including those sourced from Kubernetes ConfigMaps and Secrets, and defining inline variables or loading them from a secret. ```yaml apiVersion: opentofu.upbound.io/v1beta1 kind: Workspace metadata: name: example-remote annotations: crossplane.io/external-name: myworkspace spec: forProvider: # Use any module source supported by tofu init -from-module. source: Remote module: https://github.com/upbound/tf-example # Environment variables can be passed through env: - name: TF_VAR_varFromValue value: 'value' - name: ENV_FROM_CONFIGMAP configMapKeyRef: namespace: my-namespace name: my-config-map key: target-key - name: ENV_FROM_SECRET secretKeyRef: namespace: my-namespace name: my-secret key: target-key # Variables can be specified inline as a list of key-value pairs or as an json object, or loaded from a ConfigMap or Secret. vars: - key: region value: us-west-1 varmap: account: region: us-west-1 owners: - example-owner-1 - example-owner-2 varFiles: - source: SecretKey secretKeyRef: namespace: default name: opentofu key: example.tfvar.json # All outputs are written to the connection secret. writeConnectionSecretToRef: namespace: default name: opentofu-workspace-example-inline ``` -------------------------------- ### Configure Tofu CLI with Kubernetes Secrets Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Configuration.md Enable Tofu CLI configuration by installing it from a Kubernetes secret. The filename `.tofurc` is the conventional name for the Tofu CLI configuration file. ```yaml spec: credentials: - filename: .tofurc # use exactly this filename by convention source: Secret secretRef: namespace: upbound-system name: tofurc key: .tofurc ``` -------------------------------- ### Patching Workspace Output to Composite Resource Status Source: https://context7.com/upbound/provider-opentofu/llms.txt Example of a composition patch to read a value (e.g., `vpc_id`) from a Workspace output and map it into the Composite Resource status. ```yaml # Composition patch — read vpc_id from Workspace output into XR status patches: - type: ToCompositeFieldPath fromFieldPath: status.atProvider.outputs.vpc_id toFieldPath: status.share.vpcId policy: fromFieldPath: Optional ``` -------------------------------- ### Configure External Secret Store (Vault) Source: https://context7.com/upbound/provider-opentofu/llms.txt Enable the external secret store feature to route sensitive outputs to Vault. This example shows ControllerConfig for enabling the feature and StoreConfig for pointing to a Vault instance. ```yaml # ControllerConfig enabling ESS apiVersion: pkg.crossplane.io/v1alpha1 kind: ControllerConfig metadata: name: opentofu-config spec: args: - "--enable-external-secret-stores" metadata: annotations: vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/agent-inject-token: "true" vault.hashicorp.com/role: "crossplane" vault.hashicorp.com/agent-run-as-user: "2000" --- # StoreConfig pointing at Vault apiVersion: opentofu.upbound.io/v1beta1 kind: StoreConfig metadata: name: vault spec: type: Vault defaultScope: crossplane-system vault: server: http://vault.vault-system:8200 mountPath: secret/ version: v2 auth: method: Token token: source: Filesystem fs: path: /vault/secrets/token ``` -------------------------------- ### Workspace with Inline AWS VPC Module and Variables Source: https://context7.com/upbound/provider-opentofu/llms.txt Define an inline Workspace for an AWS VPC, including variables for customization. This example demonstrates passing a `vpcName` variable and defining outputs for `vpc_id` and `subnet_data`. ```yaml apiVersion: opentofu.upbound.io/v1beta1 kind: Workspace metadata: name: sample-inline-aws spec: providerConfigRef: name: aws-eu-west-1 forProvider: source: Inline module: | variable "vpcName" { type = string } resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" tags = { Name = var.vpcName } } resource "aws_subnet" "main" { vpc_id = aws_vpc.main.id cidr_block = "10.0.1.0/24" } output "vpc_id" { value = aws_vpc.main.id } output "subnet_data" { value = { "id" = aws_subnet.main.id, "arn" = aws_subnet.main.arn } } vars: - key: vpcName value: sample-tf-inline ``` -------------------------------- ### Terraform Outputs Example Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Configuration.md Defines various Terraform outputs including strings, numbers, objects, tuples, booleans, and sensitive values. Non-sensitive outputs are mapped to the status.atProvider.outputs section. ```hcl output "string" { value = "bar" sensitive = false } output "number" { value = 1.9 sensitive = false } output "object" { // This will be a JSON string - the key/value pairs are not accessible value = {"a": 3, "b": 2} sensitive = false } output "tuple" { // This will be a JSON string - the elements will not be accessible value = ["foo", "bar"] sensitive = false } output "bool" { value = false sensitive = false } output "sensitive" { value = "SENSITIVE" sensitive = true } ``` -------------------------------- ### Workspace with Remote Module Source Source: https://context7.com/upbound/provider-opentofu/llms.txt Define a remote Workspace that fetches a module from a Git repository. This example shows how to pass inline variables, use variable files from ConfigMaps and Secrets, and set environment variables. ```yaml # workspace-remote.yaml apiVersion: opentofu.upbound.io/v1beta1 kind: Workspace metadata: name: example-remote annotations: crossplane.io/external-name: myworkspace spec: forProvider: source: Remote module: git::https://github.com/crossplane/tf?ref=main # Inline key-value variables vars: - key: region value: us-west-1 # Variable files from ConfigMap (HCL format) and Secret (JSON format) varFiles: - source: ConfigMapKey configMapKeyRef: namespace: default name: opentofu key: example.tfvars - source: SecretKey format: JSON secretKeyRef: namespace: default name: opentofu key: example.tfvar.json # Environment variables (value, ConfigMap, or Secret) env: - name: TF_VAR_region value: us-west-1 - name: SECRET_TOKEN secretKeyRef: namespace: default name: my-secret key: token writeConnectionSecretToRef: namespace: default name: opentofu-workspace-example-remote ``` ```shell kubectl apply -f workspace-remote.yaml kubectl get workspace example-remote # NAME READY SYNCED AGE # example-remote True True 2m ``` -------------------------------- ### Terraform Module with Relative Path Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Configuration.md An example of a Terraform module definition that uses a relative path to reference another module within the same repository. This is often used in conjunction with a custom entrypoint. ```hcl module "relative-path-iam" { source = "../iam" iamRole = var.iamRole } ``` -------------------------------- ### Workspace with Custom Entrypoint Source: https://context7.com/upbound/provider-opentofu/llms.txt Specify an `entrypoint` to direct `tofu init` and `tofu apply` to a specific subdirectory within a remote module repository. This is useful when a repository contains multiple modules. ```yaml apiVersion: opentofu.upbound.io/v1beta1 kind: Workspace metadata: name: relative-path-test spec: forProvider: source: Remote module: git::https://github.com/crossplane/provider-opentofu-test-module.git entrypoint: relative-path-iam # tofu runs in this subdirectory vars: - key: iamRole value: relative-path-test ``` ```hcl # relative-path-iam/main.tf module "relative-path-iam" { source = "../iam" # relative path to sibling module iamRole = var.iamRole } ``` -------------------------------- ### ProviderConfig with Partial Backend Configuration Source: https://context7.com/upbound/provider-opentofu/llms.txt Configure a ProviderConfig with a partial backend configuration, where the remaining init arguments are supplied via a `backendFile`. This is useful for managing backend settings separately from the main provider configuration. ```yaml apiVersion: opentofu.upbound.io/v1beta1 kind: ProviderConfig metadata: name: default spec: credentials: - filename: gcp-credentials.json source: Secret secretRef: namespace: upbound-system name: gcp-creds key: credentials configuration: | provider "google" { credentials = "gcp-credentials.json" project = "official-provider-testing" } terraform { backend "kubernetes" {} } backendFile: | secret_suffix = "providerconfig-default" namespace = "upbound-system" in_cluster_config = true ``` -------------------------------- ### Tail Provider Logs for Tofu Output Source: https://context7.com/upbound/provider-opentofu/llms.txt Use `kubectl logs -f` to tail the provider logs in real time and observe `tofu` output. ```shell # Tail the provider logs to watch tofu output in real time kubectl logs -n upbound-system -f deploy/provider-opentofu-* ``` -------------------------------- ### Verify Workspace Resource Status Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Quickstart.md Check the status of the `Workspace` resource using `kubectl get workspace`. `READY` and `SYNCED` should be `True` to indicate successful application of Terraform code. ```shell $ kubectl get workspace NAME READY SYNCED AGE example-inline True True 46s ``` -------------------------------- ### Configure Provider with Private Git Credentials Source: https://context7.com/upbound/provider-opentofu/llms.txt Configure the OpenTofu provider to use private Git repositories by mounting a `.git-credentials` file as a Secret. The `filename` must exactly match `.git-credentials`. ```yaml apiVersion: opentofu.upbound.io/v1beta1 kind: ProviderConfig metadata: name: default spec: credentials: - filename: .git-credentials # exact filename required source: Secret secretRef: namespace: crossplane-system name: git-credentials key: .git-credentials configuration: | terraform { backend "kubernetes" { secret_suffix = "providerconfig-default" namespace = "crossplane-system" in_cluster_config = true } } ``` -------------------------------- ### Create OpenTofu ProviderConfig Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Quickstart.md Define a ProviderConfig to attach GCP credentials to the OpenTofu provider. Ensure the `project_id` in the credentials matches the `project` in the HCL configuration. ```yaml apiVersion: opentofu.upbound.io/v1beta1 kind: ProviderConfig metadata: name: default spec: # Note that unlike most provider configs this one supports an array of # credentials. This is because each tofu workspace uses a single # Crossplane provider config, but could use multiple Terraform providers each # with their own credentials. credentials: - filename: gcp-credentials.json source: Secret secretRef: namespace: upbound-system name: opentofu-gcp-creds key: credentials # This optional configuration block can be used to inject HCL into any # workspace that uses this provider config, for example to setup Terraform # providers. configuration: | provider "google" { credentials = "gcp-credentials.json" project = "YOUR-GCP-PROJECT-ID" } // Modules _must_ use remote state. The provider does not persist state. terraform { backend "kubernetes" { secret_suffix = "providerconfig-default" namespace = "upbound-system" in_cluster_config = true } } ``` -------------------------------- ### Verify ProviderConfig with kubectl describe Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Quickstart.md Use `kubectl describe providerconfigs` to verify the applied ProviderConfig. Check that the details match your secret and desired configuration. ```yaml $ kubectl describe providerconfigs Name: default Namespace: API Version: opentofu.upbound.io/v1beta1 Kind: ProviderConfig # Output truncated Spec: Configuration: provider "google" { credentials = "gcp-credentials.json" project = "official-provider-testing" } // Modules _must_ use remote state. The provider does not persist state. opentofu { backend "kubernetes" { secret_suffix = "providerconfig-default" namespace = "upbound-system" in_cluster_config = true } } Credentials: Filename: gcp-credentials.json Secret Ref: Key: credentials Name: gcp-creds Namespace: upbound-system Source: Secret Plugin Cache: true ``` -------------------------------- ### Recommended Module Version Pinning Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/RemoteModulePullPolicy.md Best practice for production environments is to pin module versions using specific version tags or commit SHAs. Avoid floating references. ```yaml # Recommended - specific version tag module: git::https://github.com/terraform-aws-modules/terraform-aws-vpc?ref=v5.1.2 # Recommended - specific commit module: git::https://github.com/org/repo?ref=abc123def # Avoid - floating ref module: git::https://github.com/org/repo?ref=main ``` -------------------------------- ### OpenTofu Workspace CLI Arguments Configuration Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Configuration.md Configures additional arguments for OpenTofu CLI commands like init, plan, apply, and destroy within a Workspace resource. Arguments are passed as lists of strings. ```yaml apiVersion: opentofu.upbound.io/v1beta1 kind: Workspace metadata: name: example-args spec: forProvider: # Run the opentofu init command with -upgrade=true to upgrade any stored providers initArgs: - -upgrade=true # Run the opentofu plan command with the -parallelism=2 argument planArgs: - -parallelism=2 # Run the opentofu apply command with the -target=specificresource argument applyArgs: - -target=specificresource # Run the opentofu destroy command with the -refresh=false argument destroyArgs: - -refresh=false # Use any module source supported by opentofu init -from-module. source: Remote module: https://github.com/crossplane/tf # All outputs are written to the connection secret. writeConnectionSecretToRef: namespace: default name: opentofu-workspace-example-inline ``` -------------------------------- ### Custom Entrypoint for OpenTofu Workspace Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Configuration.md Specifies a custom entrypoint directory for initializing and applying Terraform code within a Git repository. This is useful for modules located in subdirectories. ```yaml apiVersion: opentofu.upbound.io/v1beta1 kind: Workspace metadata: name: relative-path-test spec: forProvider: module: git::https://github.com/crossplane/provider-opentofu-test-module.git source: Remote entrypoint: relative-path-iam vars: - key: iamRole value: relative-path-test ``` -------------------------------- ### Enable OpenTofu CLI Logging in Workspace Source: https://context7.com/upbound/provider-opentofu/llms.txt Set `enableTofuCLILogging: true` to write all `tofu` command output to the provider container's logs. This is useful for debugging long-running applies or unexpected plan diffs. ```yaml apiVersion: opentofu.upbound.io/v1beta1 kind: Workspace metadata: name: example-random-generator annotations: crossplane.io/external-name: random spec: forProvider: source: Inline enableTofuCLILogging: true module: | resource "random_id" "example_id" { byte_length = 8 } resource "random_password" "password" { length = 16 special = true } output "random_id_hex" { value = random_id.example_id.hex } output "random_password" { value = random_password.password sensitive = true # Only in connection secret, not in status.atProvider.outputs } writeConnectionSecretToRef: namespace: default name: opentofu-workspace-random-generator ``` -------------------------------- ### Apply Workspace Configuration Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/RemoteModulePullPolicy.md Apply the updated workspace configuration to your Kubernetes cluster using `kubectl apply`. ```bash kubectl apply -f workspace.yaml ``` -------------------------------- ### Combine Version Pinning with IfNotPresent Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/RemoteModulePullPolicy.md For maximum cost savings, combine version pinning with the `IfNotPresent` remote pull policy. This ensures modules are only downloaded when necessary and specific versions are used. ```yaml spec: forProvider: source: Remote module: git::https://github.com/org/repo?ref=v1.0.0 remotePullPolicy: IfNotPresent ``` -------------------------------- ### Workspace with OpenTofu CLI Arguments Source: https://context7.com/upbound/provider-opentofu/llms.txt Customize OpenTofu CLI commands by providing extra flags through `initArgs`, `planArgs`, `applyArgs`, and `destroyArgs`. These arguments are appended to the provider's default flags. ```yaml apiVersion: opentofu.upbound.io/v1beta1 kind: Workspace metadata: name: example-args spec: forProvider: source: Remote module: https://github.com/crossplane/tf initArgs: - -upgrade=true # tofu init -upgrade=true ... planArgs: - -parallelism=2 # tofu plan -parallelism=2 ... applyArgs: - -target=specificresource # tofu apply -target=specificresource ... destroyArgs: - -refresh=false # tofu destroy -refresh=false ... writeConnectionSecretToRef: namespace: default name: opentofu-workspace-example-args ``` -------------------------------- ### Deleting an OpenTofu Workspace Source: https://context7.com/upbound/provider-opentofu/llms.txt Demonstrates how to delete an OpenTofu Workspace resource using kubectl, which triggers a `tofu destroy` operation to remove managed cloud resources. ```shell kubectl delete -f workspace-inline-gcp.yaml # Verify the workspace and underlying cloud resources are gone kubectl get workspace # No resources found ``` -------------------------------- ### Verify Remote Module Download Behavior Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Troubleshooting.md Check workspace logs for messages indicating whether the remote module was downloaded or skipped. You can also inspect the workspace status field for remote source information. ```bash # Check workspace logs kubectl logs -n upbound-system deploy/provider-opentofu-* | grep "Remote module" # First reconciliation: "Remote module downloaded" # Subsequent: "Remote module already present, skipping download" # Check status field kubectl get workspace -o jsonpath='{.status.atProvider.remoteSource}' ``` -------------------------------- ### Monitor Network Costs with kubectl logs Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/RemoteModulePullPolicy.md Monitor workspace reconciliation logs to verify cost savings after enabling `IfNotPresent`. Look for messages indicating module download status. ```bash # Example: Check workspace reconciliation logs kubectl logs -n upbound-system deploy/provider-opentofu-* | grep "Remote module" # Expected with IfNotPresent: # First reconciliation: "Remote module downloaded" # Subsequent reconciliations: "Remote module already present, skipping download" ``` -------------------------------- ### Troubleshoot Module Not Updating Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/RemoteModulePullPolicy.md Check workspace reconciliation status and logs if the module is not updating after a URL change. Verify that the provider detects the URL change. ```bash kubectl describe workspace kubectl logs -n upbound-system deploy/provider-opentofu-* | grep "Remote module URL changed" ``` -------------------------------- ### Check Workspace Status for Remote Source Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/RemoteModulePullPolicy.md Retrieve the `remoteSource` from the workspace status to verify if it's populated. This field is populated after the first successful download. ```bash kubectl get workspace -o jsonpath='{.status.atProvider.remoteSource}' ``` -------------------------------- ### Enable OpenTofu CLI Logging Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Configuration.md To enable OpenTofu CLI logging, set `enableTofuCLILogging` to `true` in the `Workspace` spec. This writes OpenTofu CLI command output to container logs for debugging. The default is `false`. ```yaml apiVersion: opentofu.upbound.io/v1beta1 kind: Workspace metadata: name: example-random-generator annotations: meta.upbound.io/example-id: opentofu/v1beta1/workspace crossplane.io/external-name: random spec: forProvider: source: Inline enableTofuCLILogging: true ... ``` -------------------------------- ### Create Kubernetes Secret with GCP Credentials Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Quickstart.md Use this `kubectl` command to create a Kubernetes secret named `opentofu-gcp-creds` in the `upbound-system` namespace, using the previously saved `gcp-credentials.json` file. ```shell kubectl create secret generic opentofu-gcp-creds -n upbound-system \ --from-file=credentials=./gcp-credentials.json ``` -------------------------------- ### Composition with OpenTofu Workspace Source: https://context7.com/upbound/provider-opentofu/llms.txt Defines a Crossplane Composition that uses an OpenTofu Workspace to manage AWS VPC and Subnet resources. It patches XR spec fields to Workspace variables and Workspace outputs to XR status. ```yaml apiVersion: apiextensions.crossplane.io/v1 kind: Composition metadata: name: tf-xsubnets.aws.demo.upbound.io labels: provider: aws implementation: opentofu spec: compositeTypeRef: apiVersion: aws.demo.upbound.io/v1alpha1 kind: XSubnet resources: - name: tf-vpc-and-subnet base: apiVersion: opentofu.upbound.io/v1beta1 kind: Workspace spec: forProvider: source: Inline module: | variable "vpcName" { type = string } resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" tags = { Name = var.vpcName } } resource "aws_subnet" "main" { vpc_id = aws_vpc.main.id cidr_block = "10.0.1.0/24" } output "vpc_id" { value = aws_vpc.main.id } output "subnet_id" { value = aws_subnet.main.id } vars: - key: vpcName patches: # Patch XR spec field → Workspace variable value - fromFieldPath: spec.vpcName toFieldPath: spec.forProvider.vars[0].value # Patch Workspace output → XR status (for sharing with other composed resources) - type: ToCompositeFieldPath fromFieldPath: status.atProvider.outputs.vpc_id toFieldPath: status.share.vpcId policy: fromFieldPath: Optional - type: ToCompositeFieldPath fromFieldPath: status.atProvider.outputs.subnet_id toFieldPath: status.share.subnetId policy: fromFieldPath: Optional ``` -------------------------------- ### OpenTofu Output Types Source: https://context7.com/upbound/provider-opentofu/llms.txt Demonstrates various output types in OpenTofu, including string, number, boolean, object, tuple, and sensitive outputs. Sensitive outputs are only written to the connection secret. ```hcl # Inline module demonstrating output types output "string" { value = "bar"; sensitive = false } output "number" { value = 1.9; sensitive = false } output "bool" { value = false; sensitive = false } output "object" { value = {"a": 3, "b": 2}; sensitive = false } output "tuple" { value = ["foo", "bar"]; sensitive = false } output "sensitive" { value = "SECRET"; sensitive = true } ``` -------------------------------- ### IfNotPresent with Entrypoint Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/RemoteModulePullPolicy.md Configure the `IfNotPresent` policy when using an `entrypoint`. The `.terraform.lock.hcl` file check will be performed within the specified entrypoint subdirectory, ensuring correct module initialization status detection even with complex workspace structures. ```yaml spec: forProvider: source: Remote module: git::https://github.com/terraform-aws-modules/terraform-aws-vpc.git?ref=v5.1.2 entrypoint: examples/complete remotePullPolicy: IfNotPresent ``` -------------------------------- ### Configure OpenTofu Provider with AWS IRSA Source: https://context7.com/upbound/provider-opentofu/llms.txt Set up the OpenTofu provider to use AWS IAM Roles for Service Accounts (IRSA) for authentication. This configuration avoids the need for static AWS credentials. ```yaml apiVersion: pkg.crossplane.io/v1alpha1 kind: ControllerConfig metadata: name: opentofu-config annotations: eks.amazonaws.com/role-arn: arn:aws:iam::ACCOUNTID:role/provider-opentofu spec: args: - "--debug" podSecurityContext: fsGroup: 2000 --- apiVersion: pkg.crossplane.io/v1 kind: Provider metadata: name: crossplane-provider-opentofu spec: package: xpkg.upbound.io/upbound/provider-opentofu:v0.1.0 controllerConfigRef: name: opentofu-config --- apiVersion: opentofu.upbound.io/v1beta1 kind: ProviderConfig metadata: name: aws-irsa spec: credentials: - filename: aws.json source: None # OpenTofu natively uses IRSA — no static creds needed configuration: | terraform { backend "kubernetes" { secret_suffix = "providerconfig-tf-aws" namespace = "crossplane-system" in_cluster_config = true } } provider "aws" {} ``` -------------------------------- ### Workspace with Inline GCP Module Source Source: https://context7.com/upbound/provider-opentofu/llms.txt Define an inline Workspace that embeds the full contents of `main.tf` directly in the manifest. Ideal for simple, self-contained modules. Non-sensitive outputs appear in `status.atProvider.outputs`; sensitive outputs go to the connection secret. ```yaml # workspace-inline-gcp.yaml apiVersion: opentofu.upbound.io/v1beta1 kind: Workspace metadata: name: example-inline annotations: # The OpenTofu workspace name. Defaults to metadata.name when omitted. crossplane.io/external-name: coolbucket spec: forProvider: source: Inline module: | resource "random_id" "example" { byte_length = 4 } resource "google_storage_bucket" "example" { name = "crossplane-example-${opentofu.workspace}-${random_id.example.hex}" location = "US" force_destroy = true public_access_prevention = "enforced" } output "url" { value = google_storage_bucket.example.self_link } writeConnectionSecretToRef: namespace: default name: opentofu-workspace-example-inline ``` ```shell kubectl apply -f workspace-inline-gcp.yaml kubectl get workspace # NAME READY SYNCED AGE # example-inline True True 46s # Read non-sensitive outputs kubectl get workspace example-inline -o jsonpath='{.status.atProvider.outputs}' ``` -------------------------------- ### Configure Controller Performance with ControllerConfig Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Configuration.md Tune provider performance by setting polling intervals and maximum concurrent reconciliation rates. Ensure the ControllerConfig is referenced by the Provider. ```yaml apiVersion: pkg.crossplane.io/v1alpha1 kind: ControllerConfig metadata: name: opentofu labels: app: crossplane-provider-opentofu spec: args: - -d - --poll=5m - --max-reconcile-rate=10 ``` ```yaml apiVersion: pkg.crossplane.io/v1 kind: Provider metadata: name: provider-opentofu spec: package: xpkg.upbound.io/upbound/provider-opentofu: controllerConfigRef: name: opentofu ``` -------------------------------- ### View Kubernetes Secret Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Quickstart.md This command displays the details of the created Kubernetes secret, confirming its creation and size. ```shell kubectl describe secret gcp-creds -n upbound-system ``` -------------------------------- ### Reference Vault StoreConfig in Workspace Source: https://context7.com/upbound/provider-opentofu/llms.txt A Workspace resource referencing a Vault StoreConfig via `publishConnectionDetailsTo`. This allows sensitive outputs from the Workspace to be stored in Vault. ```yaml # Workspace referencing the Vault StoreConfig via publishConnectionDetailsTo apiVersion: opentofu.upbound.io/v1beta1 kind: Workspace metadata: name: foo spec: forProvider: source: Inline module: | output "db_password" { value = "super-secret" sensitive = true } publishConnectionDetailsTo: name: bar configRef: name: vault # references the StoreConfig above ``` -------------------------------- ### Create Secret for Private Git Credentials Source: https://context7.com/upbound/provider-opentofu/llms.txt Create a Kubernetes secret named `git-credentials` from a local `.git-credentials` file. The filename within the secret must be `.git-credentials` for the provider to automatically detect it. ```shell # Create the secret from an existing .git-credentials file cat .git-credentials # https://:@github.com kubectl create secret generic git-credentials \ --from-file=.git-credentials ``` -------------------------------- ### Enable External Secret Stores with Vault Source: https://github.com/upbound/provider-opentofu/blob/main/docs/monolith/Configuration.md Enable external secret store support by passing the `--enable-external-secret-stores` flag to the controller. This requires a `StoreConfig` for Vault and specification in `publishConnectionDetailsTo`. ```yaml apiVersion: pkg.crossplane.io/v1alpha1 kind: ControllerConfig metadata: name: opentofu-config labels: app: crossplane-provider-opentofu spec: image: crossplane/provider-opentofu-controller:v0.1.0 args: - -d - --enable-external-secret-stores metadata: annotations: vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/agent-inject-token: "true" vault.hashicorp.com/role: "crossplane" vault.hashicorp.com/agent-run-as-user: "2000" ``` ```yaml apiVersion: opentofu.upbound.io/v1beta1 kind: StoreConfig metadata: name: vault spec: type: Vault defaultScope: crossplane-system vault: server: http://vault.vault-system:8200 mountPath: secret/ version: v2 auth: method: Token token: source: Filesystem fs: path: /vault/secrets/token ``` ```yaml apiVersion: apiextensions.crossplane.io/v1 kind: Composition metadata: name: ... labels: feature: ess spec: compositeTypeRef: apiVersion: ... kind: ... resources: - name: foo base: apiVersion: opentofu.upbound.io/v1beta1 kind: Workspace metadata: name: foo spec: forProvider: ... publishConnectionDetailsTo: name: bar configRef: name: vault ```