Converting your Deployment Manager configurations with DM Convert

This page describes the process of using DM Convert to convert your Deployment Manager configurations to Kubernetes Resource Model (KRM) or Terraform.

Set up your environment

Set up your environment variables

Save the following environment variables, which the rest of this guide uses:

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID \
 --format="value(projectNumber)")
export DM_CONVERT_IMAGE="us-central1-docker.pkg.dev/\
dm-convert-host/deployment-manager/dm-convert:public-preview"

# Enable the Cloud Build API
gcloud services enable cloudbuild.googleapis.com

# Grant Cloud Build service account permission to manage Google Cloud Storage
# bucket and DM deployments
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member=serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
--role=roles/storage.admin

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member=serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
--role=roles/deploymentmanager.editor

Set up your tools

You must have access to the following tools:

  • gcloud

  • gsutil

  • docker

  • kubectl

If you use Cloud Shell to run DM Convert, you already have access to them.

Open in Cloud Shell

Convert your configurations

At a high level, you migrate your Deployment Manager configuration to Terraform or KRM by:

  1. Preparing a Deployment Manager deployment for conversion.

  2. Converting the configuration to either the HCL (HashiCorp configuration language, for Terraform) or the KRM (Kubernetes Resource Model) format.

  3. Using Terraform or Config Connector to apply the converted configuration.

  4. Abandoning the existing Deployment Manager deployment.

Prepare your existing deployment

DM Convert operates on Deployment Manager configuration files and templates. To provide these files and templates to DM Convert, you use a Cloud Storage bucket.

To create a Cloud Storage bucket to store your Deployment Manager configuration at $BUCKET_URI, run the following commands:

export BUCKET_NAME=$PROJECT_ID-dm-config
export BUCKET_URI=gs://$BUCKET_NAME
gsutil mb $BUCKET_URI

# This is where we will store DM configuration to be converted
export DM_CONFIG_URI=$BUCKET_URI/dm

You can add a configuration file directly to the Cloud Storage bucket, or acquire a configuration from a live deployment.

Convert a configuration file

You can use the following sample configuration to try out the converter. Replace PROJECT_ID with your Google Cloud project ID, and save the below contents to a file called deployment.yaml:

resources:
- name: bigquerydataset
  type: bigquery.v2.dataset
  properties:
    datasetReference:
      datasetId: bigquerydataset
      projectId: PROJECT_ID
    defaultTableExpirationMs: 36000000
    location: us-west1
- type: bigquery.v2.table
  name: bigquerytable
  properties:
    datasetId: bigquerydataset
    labels:
      data-source: external
      schema-type: auto-junk
    tableReference:
      projectId: PROJECT_ID
      tableId: bigquerytable
  metadata:
    dependsOn:
    - bigquerydataset

After you save the file, copy it to the Cloud Storage bucket you previously created:

gsutil cp ./deployment.yaml $DM_CONFIG_URI/deployment.yaml

Grant the Identity and Access Management (IAM) role roles/bigquery.dataEditor to your Cloud Build service account:

gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com \
--role=roles/bigquery.dataEditor

Acquire a configuration from a live deployment

If you want to acquire and convert the configuration of a live deployment, you can retrieve the expanded configuration and save it to disk by running the following commands, replacing DEPLOYMENT_NAME with the name of the deployment.

Cloud Build

export DEPLOYMENT_NAME=[DEPLOYMENT_NAME]
export MANIFEST_NAME=$(gcloud deployment-manager deployments \
 describe $DEPLOYMENT_NAME \
  --project $PROJECT_ID --format="value(deployment.manifest)" |\
  grep -oEi "manifest-[0-9]+")
gcloud deployment-manager manifests describe $MANIFEST_NAME \
  --deployment $DEPLOYMENT_NAME --project $PROJECT_ID \
  --format="value(expandedConfig)" > deployment.yaml

# Grant additional roles to allow the Cloud Build service account to manage the
# resources in your deployment. For a list of roles, see
# https://cloud.google.com/iam/docs/understanding-roles. 
# For example:
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com \
--role=roles/pubsub.editor

# Alternatively, you may grant roles/editor for testing purposes:
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com \
--role=roles/editor

Alternate

# Configure your project/deployment
DEPLOYMENT_NAME=foo
PROJECT_ID=bar

# Fetch the latest manifest for the given deployment
gcloud deployment-manager deployments describe $DEPLOYMENT_NAME \
  --project $PROJECT_ID --format="value(deployment.manifest)"
https://www.googleapis.com/deploymentmanager/v2/projects/$PROJECT_ID/global/deployments/bq/manifests/manifest-1618872644848

# The manifest name is the last path segment from the URI
# in the above command output
MANIFEST_NAME="manifest-1618872644848"
# Save the expanded manifest to deployment.yaml
gcloud deployment-manager manifests describe $MANIFEST_NAME \
  --deployment $DEPLOYMENT_NAME --project $PROJECT_ID \
  --format="value(expandedConfig)" > deployment.yaml

After you save the file, copy it to the Cloud Storage bucket you previously created:

gsutil cp ./deployment.yaml $DM_CONFIG_URI/deployment.yaml

Convert your existing deployment

This guide uses Cloud Build for the conversion process.

The build definition reads the Deployment Manager configuration from the Cloud Storage bucket $DM_CONFIG_URI, converts it to KRM or Terraform format, and saves the output in the same Cloud Storage bucket.

Copy the following contents into a file called cloudbuild_convert.yaml:

steps:
- id: '[FETCH_DM_CONFIG]'
  name: 'gcr.io/cloud-builders/gsutil'
  entrypoint: 'sh'
  args:
  - '-c'
  - |
      mkdir dm
      gsutil cp "${_BUCKET_URI}/dm/*" dm/.
      mkdir "${_OUTPUT_FORMAT}"

- id: '[CONVERT_DM_CONFIG]'
  name: '${_DM_CONVERT_IMAGE}'
  args: ['--config', './dm/deployment.yaml',
         '--project_id', '${PROJECT_ID}',
          '--project_number', '${PROJECT_NUMBER}',
          '--output_format', '${_OUTPUT_FORMAT}',
          '--output_file', '${_OUTPUT_FORMAT}/${_OUTPUT_FILE}',
          '--deployment_name', '${_DEPLOYMENT_NAME}']

- id: '[EXPORT_OUTPUT_TO_STORAGE_BUCKET]'
  name: 'gcr.io/cloud-builders/gsutil'
  entrypoint: 'sh'
  args:
  - '-c'
  - |
      gsutil cp "${_OUTPUT_FORMAT}/${_OUTPUT_FILE}" \
        "${_BUCKET_URI}/output/${_OUTPUT_FILE}"

You can invoke Cloud Build from the same folder as cloudbuild_convert.yaml by providing the following parameters:

  • _BUCKET_URI: The Cloud Storage bucket to read a Deployment Manager configuration from and save the converted output into.

  • _OUTPUT_FORMAT: The output format for the conversion. This can be either KRM for KRM or TF for Terraform.

  • _OUTPUT_FILE: The name of the file to which the converted output will be saved in the Cloud Storage bucket.

  • _DM_CONVERT_IMAGE: The container image to use.

  • _DEPLOYMENT_NAME: The name of the deployment. This is relevant if you're using templates in your Deployment Manager configuration, and are also using the deployment environment variable. For more information, visit Using an environment variable.

Sample conversions

KRM

To convert the configuration to the KRM format and save it to $BUCKET_URI/output/krm_resources.yaml, run the following command:

export KRM_RESOURCES=krm_resources.yaml

gcloud builds submit --no-source --config cloudbuild_convert.yaml \
--substitutions=_DM_CONVERT_IMAGE=$DM_CONVERT_IMAGE,_BUCKET_URI=$BUCKET_URI,\
_DEPLOYMENT_NAME=test-conversion-to-krm,\
_OUTPUT_FORMAT=KRM,_OUTPUT_FILE=$KRM_RESOURCES

# Print output file
gsutil cat $BUCKET_URI/output/$KRM_RESOURCES

Terraform

To convert the configuration to the HCL format and save it as $BUCKET_URI/output/tf_resources.tf, run the following command:

export TF_RESOURCES=tf_resources.tf

gcloud builds submit --no-source --config cloudbuild_convert.yaml \
--substitutions=_DM_CONVERT_IMAGE=$DM_CONVERT_IMAGE,_BUCKET_URI=$BUCKET_URI,\
_DEPLOYMENT_NAME=test-conversion-to-tf,\
_OUTPUT_FORMAT=TF,_OUTPUT_FILE=$TF_RESOURCES

# Print output file
gsutil cat $BUCKET_URI/output/$TF_RESOURCES

Apply your converted configuration

KRM

Set up Config Connector

To actuate the resources in the KRM configuration files, you need a Kubernetes cluster with Config Connector installed. To create a test cluster, refer to Installing with the GKE add-on.

In Cloud Shell, ensure that your kubectl credentials are configured for the GKE cluster that you want to use. Replace GKE_CLUSTER with the name of the cluster, and run the following command:

gcloud container clusters get-credentials GKE_CLUSTER

Deploy your converted KRM configuration by using kubectl

To deploy your converted KRM configuration using kubectl, run the following commands:

# Copy the converted configuration from the storage bucket
gsutil cp $BUCKET_URI/output/$KRM_RESOURCES $KRM_RESOURCES

# Ensure that the namespace is annotated to create resources in the correct
# project/folder/organization. https://cloud.google.com/config-connector/docs/how-to/install-upgrade-uninstall#specify
kubectl apply -n CONFIG_CONNECTOR_NAMESPACE -f $KRM_RESOURCES

# Wait for the resources to become healthy
kubectl wait -n CONFIG_CONNECTOR_NAMESPACE --for=condition=Ready \
  --timeout=5m -f $KRM_RESOURCES

Terraform

Set up Terraform

Use the Cloud Storage bucket you previously created to configure Terraform with your Google Cloud project:

# Configure default project
cat <<EOF > echo > main.tf
provider "google" {
project = "$PROJECT_ID"
}
EOF

# Configure Cloud Storage remote backend to store state in gs://$BUCKET_NAME/tfstate
cat <<EOF > echo > backend.tf
terraform {
backend "gcs" {
  bucket = "$BUCKET_NAME"
  prefix = "tfstate"
}
}
EOF

Deploy your converted configuration by using Terraform

Create a local file called cloudbuild_tf.yaml in the same folder as the main.tf and backend.tf files that you previously created, and copy the following into it:

steps:
- id: '[FETCH_DM_CONFIG]'
  name: 'gcr.io/cloud-builders/gsutil'
  entrypoint: 'sh'
  args:
  - '-c'
  - |
      # Fetch the latest manifest for the given deployment
      gsutil cp $_BUCKET_URI/output/*.tf .

- id: '[APPLY_USING_TERRAFORM]'
  name: 'hashicorp/terraform'
  entrypoint: 'sh'
  args: 
  - '-c'
  - | 
      echo "***************  TERRAFORM INIT  ******************"
      # NOTE: if Terraform state gets corrupted during testing,
      # use init --reconfigure to reset backend
      terraform init
      echo "***************  TERRAFORM PLAN  ******************"
      terraform plan
      echo "**************  TERRAFORM APPLY  ******************"
      terraform apply --auto-approve

Import existing resources

If you're converting an existing deployment, use terraform import RESOURCE_TYPE RESOURCE_ID to import the existing resources instead of provisioning them again by using terraform apply.

For example, if your existing deployment was:

resources:
  - type: pubsub.v1.topic
    name: sample-pubsub-topic
    properties:
      topic: sample-pubsub-topic
  - type: pubsub.v1.subscription
    name: sample-pubsub-subscription
    properties:
      topic: $(ref.sample-pubsub-topic.name)
      subscription: sample-pubsub-subscription

According to the import section of the Terraform documentation for google_pubsub_topic, terraform import can take one of the following formats:

$ terraform import google_pubsub_topic.default projects/PROJECT_NAME/topics/NAME
$ terraform import google_pubsub_topic.default PROJECT_NAME/NAME
$ terraform import google_pubsub_topic.default NAME

Make the following substitutions:

  • PROJECT_NAME is your project ID.

  • NAME is the Deployment Manager resource name, such as sample-pubsub-topic.

  • default is the Terraform resource name, which is the Deployment Manager resource name with all - characters replaced with _. You can also find the resource name by using gsutil cat $BUCKET_URI/output/$TF_RESOURCES to check the converted Terraform configuration.

After you make the substitutions, your command should be similar to:

$ terraform import google_pubsub_topic.sample_pubsub_topic
$PROJECT_ID/sample-pubsub-topic

The Terraform documentation for google_pubsub_subscription shows the following possible formats:

$ terraform import google_pubsub_subscription.default projects/PROJECT_NAME/subscriptions/NAME
$ terraform import google_pubsub_subscription.default PROJECT_NAME/NAME
$ terraform import google_pubsub_subscription.default NAME

One possible terraform import command is:

$ terraform import google_pubsub_subscription.sample_pubsub_subscription
$PROJECT_ID/sample-pubsub-subscription

The Cloud Build definition for this command is:

steps:
- id: '[FETCH_DM_CONFIG]'
  name: 'gcr.io/cloud-builders/gsutil'
  entrypoint: 'sh'
  args: 
  - '-c'
  - |
      # Fetch the latest manifest for the given deployment
      gsutil cp $_BUCKET_URI/output/*.tf .

- id: '[APPLY_USING_TERRAFORM]'
  name: 'hashicorp/terraform'
  entrypoint: 'sh'
  args: 
  - '-c'
  - | 
      echo "***************  TERRAFORM INIT  ******************"
      # NOTE: if Terraform state gets corrupted during testing,
      # use init --reconfigure to reset backend
      terraform init
      echo "***************  TERRAFORM PLAN  ******************"
      terraform plan
      echo "**************  TERRAFORM IMPORT  ******************"
      terraform import google_pubsub_topic.sample_pubsub_topic $PROJECT_ID/sample-pubsub-topic
      terraform import google_pubsub_subscription.sample_pubsub_subscription $PROJECT_ID/sample-pubsub-subscription

Optionally, you can use Deployment Manager to delete the deployment, and recreate the deployment with Terraform by using terraform apply.

You can invoke Cloud Build from the same folder as the file you previously created, by providing the following parameters:

  • _BUCKET_URI: The storage bucket where you stored your converted Terraform configuration.
gcloud builds submit . --config cloudbuild_tf.yaml \
--substitutions=_BUCKET_URI=$BUCKET_URI

Clean up

Clean up the sample dataset and table

KRM

To clean up the BigQuery dataset and table from the sample configuration, run:

# If the resource was created via Config Connector:
kubectl delete -n CONFIG_CONNECTOR_NAMESPACE -f $KRM_RESOURCES

Terraform

Save the following Cloud Build definition to cloudbuild_tf_destroy.yaml:

# If the resource was created via Terraform, save the following Cloud Build steps into cloudbuild_tf_destroy.yaml:
steps:
- id: '[FETCH_DM_CONFIG]'
  name: 'gcr.io/cloud-builders/gsutil'
  entrypoint: 'sh'
  args: 
  - '-c'
  - |
      # Fetch the latest manifest for the given deployment
      gsutil cp $_BUCKET_URI/output/*.tf .

- id: '[DESTROY_USING_TERRAFORM]'
  name: 'hashicorp/terraform'
  entrypoint: 'sh'
  args: 
  - '-c'
  - | 
      echo "***************  TERRAFORM INIT  ******************"
      terraform init
      # NOTE: if Terraform state gets corrupted during testing,
      # use init --reconfigure to reset backend

      # Remove delete protection on BigQuery table
      sed -i "/resource \"google_bigquery_table\"/a deletion_protection=\"false\"" tf_resources.tf
      terraform apply --auto-approve

      echo "***************  TERRAFORM DESTROY ****************"
      terraform destroy --auto-approve

After you save the definition, run the following command:

gcloud builds submit . --config cloudbuild_tf_destroy.yaml \
--substitutions=_BUCKET_URI=$BUCKET_URI

Clean up your deployment

To abandon a live deployment that you successfully converted to KRM or Terraform, run:

gcloud deployment-manager deployments delete DEPLOYMENT_NAME --delete-policy ABANDON

Using DM Convert locally

If you want to iterate and debug locally, you can run DM Convert locally in Docker.

KRM

To convert the configuration in deployment.yaml to the KRM format and save it to krm_resources.yaml, run the following command:

docker run --rm -it --workdir=/convert \
--volume=$(pwd):/convert \
us-central1-docker.pkg.dev/dm-convert-host/deployment-manager/dm-convert:public-preview \
--output_format krm \
--config deployment.yaml \
--output_file krm_resources.yaml

Terraform

To convert the configuration in deployment.yaml to the HCL format and save it as output.tf, run the following command:

docker run --rm -it --workdir=/convert \
--volume=$(pwd):/convert \
us-central1-docker.pkg.dev/dm-convert-host/deployment-manager/dm-convert:public-preview \
--output_format tf \
--config deployment.yaml \
--output_file output.tf

To get a list of supported resources, you can use the --list_supported_types flag for a given output format specified by using --output_format.

KRM

To list supported resources for KRM, run the following command:

docker run --rm -it \
us-central1-docker.pkg.dev/dm-convert-host/deployment-manager/dm-convert:public-preview \
--output_format krm \
--list_supported_types

Terraform

To list supported resources for Terraform, run the following command:

docker run --rm -it \
us-central1-docker.pkg.dev/dm-convert-host/deployment-manager/dm-convert:public-preview \
--output_format tf \
--list_supported_types