Octopus - Serialize Runbook to Terraform

Octopus.Script exported 2023-11-08 by mcasperson belongs to ‘Octopus’ category.

Serialize an Octopus runbook as a Terraform module and upload the resulting package to the Octopus built in feed.

Note the exported runbooks do not include project variables, so any project that the exported runbook is attached to must already have all project and library variables defined.

Parameters

When steps based on the template are included in a project’s deployment process, the parameters below can be set.

Ignore All Changes

SerializeProject.Exported.Project.IgnoreAllChanges = False

Selecting this option creates a Terraform module with the “lifecycle.ignore_changes” option set to “all”. This allows the resources to be created if they do not exist, but won’t update them if the module is reapplied.

Terraform Backend

SerializeProject.ThisInstance.Terraform.Backend = s3

The backed to define in the Terraform module.

Octopus Server URL

SerializeProject.ThisInstance.Server.Url = #{Octopus.Web.ServerUri}

The URL of the Octopus Server hosting the project to be serialized.

Octopus API Key

SerializeProject.ThisInstance.Api.Key =

The Octopus API Key

Octopus Space ID

SerializeProject.Exported.Space.Id = #{Octopus.Space.Id}

The Space ID containing the project to be exported

Octopus Project Name

SerializeProject.Exported.Project.Name = #{Octopus.Project.Name}

The name of the project containing the runbook.

Octopus Runbook Name

SerializeProject.Exported.Runbook.Name =

The name of the runbook to serialize.

Octopus Upload Space ID

SerializeProject.Octopus.UploadSpace.Id =

The ID of the space to upload the Terraform package to. Leave this blank to upload to the space defined in the Octopus Space ID parameter.

Script body

Steps based on this template will execute the following Python script.

import argparse
import os
import re
import socket
import subprocess
import sys
from datetime import datetime
from urllib.parse import urlparse
from itertools import chain
import platform
from urllib.request import urlretrieve
import zipfile

# If this script is not being run as part of an Octopus step, return variables from environment variables.
# Periods are replaced with underscores, and the variable name is converted to uppercase
if "get_octopusvariable" not in globals():
    def get_octopusvariable(variable):
        return os.environ[re.sub('\\.', '_', variable.upper())]

# If this script is not being run as part of an Octopus step, print directly to std out.
if "printverbose" not in globals():
    def printverbose(msg):
        print(msg)


def printverbose_noansi(output):
    """
    Strip ANSI color codes and print the output as verbose
    :param output: The output to print
    """
    output_no_ansi = re.sub('\x1b\[[0-9;]*m', '', output)
    printverbose(output_no_ansi)


def get_octopusvariable_quiet(variable):
    """
    Gets an octopus variable, or an empty string if it does not exist.
    :param variable: The variable name
    :return: The variable value, or an empty string if the variable does not exist
    """
    try:
        return get_octopusvariable(variable)
    except:
        return ''


def execute(args, cwd=None, env=None, print_args=None, print_output=printverbose_noansi):
    """
        The execute method provides the ability to execute external processes while capturing and returning the
        output to std err and std out and exit code.
    """
    process = subprocess.Popen(args,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE,
                               text=True,
                               cwd=cwd,
                               env=env)
    stdout, stderr = process.communicate()
    retcode = process.returncode

    if print_args is not None:
        print_output(' '.join(args))

    if print_output is not None:
        print_output(stdout)
        print_output(stderr)

    return stdout, stderr, retcode


def is_windows():
    return platform.system() == 'Windows'


def init_argparse():
    parser = argparse.ArgumentParser(
        usage='%(prog)s [OPTION] [FILE]...',
        description='Serialize an Octopus project to a Terraform module'
    )
    parser.add_argument('--ignore-all-changes',
                        action='store',
                        default=get_octopusvariable_quiet(
                            'SerializeProject.Exported.Project.IgnoreAllChanges') or get_octopusvariable_quiet(
                            'Exported.Project.IgnoreAllChanges') or 'false',
                        help='Set to true to set the "lifecycle.ignore_changes" ' +
                             'setting on each exported resource to "all"')
    parser.add_argument('--terraform-backend',
                        action='store',
                        default=get_octopusvariable_quiet(
                            'SerializeProject.ThisInstance.Terraform.Backend') or get_octopusvariable_quiet(
                            'ThisInstance.Terraform.Backend') or 'pg',
                        help='Set this to the name of the Terraform backend to be included in the generated module.')
    parser.add_argument('--server-url',
                        action='store',
                        default=get_octopusvariable_quiet(
                            'SerializeProject.ThisInstance.Server.Url') or get_octopusvariable_quiet(
                            'ThisInstance.Server.Url'),
                        help='Sets the server URL that holds the project to be serialized.')
    parser.add_argument('--api-key',
                        action='store',
                        default=get_octopusvariable_quiet(
                            'SerializeProject.ThisInstance.Api.Key') or get_octopusvariable_quiet(
                            'ThisInstance.Api.Key'),
                        help='Sets the Octopus API key.')
    parser.add_argument('--space-id',
                        action='store',
                        default=get_octopusvariable_quiet(
                            'SerializeProject.Exported.Space.Id') or get_octopusvariable_quiet(
                            'Exported.Space.Id') or get_octopusvariable_quiet('Octopus.Space.Id'),
                        help='Set this to the space ID containing the project to be serialized.')
    parser.add_argument('--project-name',
                        action='store',
                        default=get_octopusvariable_quiet(
                            'SerializeProject.Exported.Project.Name') or get_octopusvariable_quiet(
                            'Exported.Project.Name') or get_octopusvariable_quiet(
                            'Octopus.Project.Name'),
                        help='Set this to the name of the project to be serialized.')
    parser.add_argument('--runbook-name',
                        action='store',
                        default=get_octopusvariable_quiet(
                            'SerializeProject.Exported.Runbook.Name') or get_octopusvariable_quiet(
                            'Exported.Runbook.Name'),
                        help='Set this to the name of the project to be serialized.')
    parser.add_argument('--upload-space-id',
                        action='store',
                        default=get_octopusvariable_quiet(
                            'SerializeProject.Octopus.UploadSpace.Id') or get_octopusvariable_quiet(
                            'Octopus.UploadSpace.Id') or get_octopusvariable_quiet('Octopus.Space.Id'),
                        help='Set this to the space ID of the Octopus space where ' +
                             'the resulting package will be uploaded to.')

    return parser.parse_known_args()


def ensure_octo_cli_exists():
    if is_windows():
        print("Checking for the Octopus CLI")
        try:
            stdout, _, exit_code = execute(['octo', 'help'])
            printverbose(stdout)
            if not exit_code == 0:
                raise "Octo CLI not found"
        except:
            print("Downloading the Octopus CLI")
            urlretrieve('https://download.octopusdeploy.com/octopus-tools/9.0.0/OctopusTools.9.0.0.win-x64.zip',
                        'OctopusTools.zip')
            with zipfile.ZipFile('OctopusTools.zip', 'r') as zip_ref:
                zip_ref.extractall(os.getcwd())


def check_docker_exists():
    try:
        stdout, _, exit_code = execute(['docker', 'version'])
        printverbose(stdout)
        if not exit_code == 0:
            raise "Docker not found"
    except:
        print('Docker must be installed: https://docs.docker.com/get-docker/')
        sys.exit(1)


check_docker_exists()
ensure_octo_cli_exists()
parser, _ = init_argparse()

# Variable precondition checks
if len(parser.server_url) == 0:
    print("--server-url, ThisInstance.Server.Url, or SerializeProject.ThisInstance.Server.Url must be defined")
    sys.exit(1)

if len(parser.api_key) == 0:
    print("--api-key, ThisInstance.Api.Key, or ThisInstance.Api.Key must be defined")
    sys.exit(1)
    
octoterra_image = 'ghcr.io/octopussolutionsengineering/octoterra-windows' if is_windows() else 'ghcr.io/octopussolutionsengineering/octoterra'
octoterra_mount = 'C:/export' if is_windows() else '/export'  

print("Pulling the Docker images")
execute(['docker', 'pull', octoterra_image])

if not is_windows():
    execute(['docker', 'pull', 'ghcr.io/octopusdeploylabs/octo'])

# Find out the IP address of the Octopus container
parsed_url = urlparse(parser.server_url)
octopus = socket.getaddrinfo(parsed_url.hostname, '80')[0][4][0]

print("Octopus hostname: " + parsed_url.hostname)
print("Octopus IP: " + octopus.strip())

os.mkdir(os.getcwd() + '/export')

export_args = ['docker', 'run',
               '--rm',
               '--add-host=' + parsed_url.hostname + ':' + octopus.strip(),
               '-v', os.getcwd() + '/export:' + octoterra_mount,
               octoterra_image,
               # the url of the instance
               '-url', parser.server_url,
               # the api key used to access the instance
               '-apiKey', parser.api_key,
               # add a postgres backend to the generated modules
               '-terraformBackend', parser.terraform_backend,
               # dump the generated HCL to the console
               '-console',
               # dump the project from the current space
               '-space', parser.space_id,
               # the name of the project to serialize
               '-projectName', parser.project_name,
               # the name of the runbook to serialize
               '-runbookName', parser.runbook_name,
               # ignoreProjectChanges can be set to ignore all changes to the project, variables, runbooks etc
               '-ignoreProjectChanges=' + parser.ignore_all_changes,
               # for any secret variables, add a default value set to the octostache value of the variable
               # e.g. a secret variable called "database" has a default value of "#{database}"
               '-defaultSecretVariableValues',
               # detach any step templates, allowing the exported project to be used in a new space
               '-detachProjectTemplates',
               # Capture the octopus endpoint, space ID, and space name as output vars. This is useful when
               # querying th Terraform state file to know which space and instance the resources were
               # created in. The scripts used to update downstream projects in bulk work by querying the
               # Terraform state, finding all the downstream projects, and using the space name to only process
               # resources that match the current tenant (because space names and tenant names are the same).
               # The output variables added by this option are octopus_server, octopus_space_id, and
               # octopus_space_name.
               '-includeOctopusOutputVars',
               # Where steps do not explicitly define a worker pool and reference the default one, this
               # option explicitly exports the default worker pool by name. This means if two spaces have
               # different default pools, the exported project still uses the pool that the original project
               # used.
               '-lookUpDefaultWorkerPools',
               # These tenants are linked to the project to support some management runbooks, but should not
               # be exported
               '-excludeAllTenants',
               # The directory where the exported files will be saved
               '-dest', octoterra_mount]

print("Exporting Terraform module")
_, _, octoterra_exit = execute(export_args)

if not octoterra_exit == 0:
    print("Octoterra failed. Please check the logs for more information.")
    sys.exit(1)

date = datetime.now().strftime('%Y.%m.%d.%H%M%S')

print("Creating Terraform module package")
if is_windows():
    execute(['octo',
             'pack',
             '--format', 'zip',
             '--id', re.sub('[^0-9a-zA-Z]', '_', parser.project_name + "_" + parser.runbook_name),
             '--version', date,
             '--basePath', os.getcwd() + '\\export',
             '--outFolder', 'C:\\export'])
else:
    _, _, _ = execute(['docker', 'run',
                            '--rm',
                            '--add-host=' + parsed_url.hostname + ':' + octopus.strip(),
                            '-v', os.getcwd() + "/export:/export",
                            'ghcr.io/octopusdeploylabs/octo',
                            'pack',
                            '--format', 'zip',
                            '--id', re.sub('[^0-9a-zA-Z]', '_', parser.project_name + "_" + parser.runbook_name),
                            '--version', date,
                            '--basePath', '/export',
                            '--outFolder', '/export'])

print("Uploading Terraform module package")
if is_windows():
    _, _, _ = execute(['octo',
                            'push',
                            '--apiKey', parser.api_key,
                            '--server', parser.server_url,
                            '--space', parser.upload_space_id,
                            '--package', 'C:\\export\\' +
                            re.sub('[^0-9a-zA-Z]', '_', parser.project_name + "_" + parser.runbook_name) + '.' + date + '.zip',
                            '--replace-existing'])
else:
    _, _, _ = execute(['docker', 'run',
                            '--rm',
                            '--add-host=' + parsed_url.hostname + ':' + octopus.strip(),
                            '-v', os.getcwd() + "/export:/export",
                            'ghcr.io/octopusdeploylabs/octo',
                            'push',
                            '--apiKey', parser.api_key,
                            '--server', parser.server_url,
                            '--space', parser.upload_space_id,
                            '--package', '/export/' +
                            re.sub('[^0-9a-zA-Z]', '_', parser.project_name + "_" + parser.runbook_name) + '.' + date + '.zip',
                            '--replace-existing'])

print("##octopus[stdout-default]")

print("Done")

Provided under the Apache License version 2.0.

Report an issue

To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.

{
  "Id": "07b966c3-130c-4f13-ae0f-5105af5b97a1",
  "Name": "Octopus - Serialize Runbook to Terraform",
  "Description": "Serialize an Octopus runbook as a Terraform module and upload the resulting package to the Octopus built in feed.\n\nNote the exported runbooks do not include project variables, so any project that the exported runbook is attached to must already have all project and library variables defined.",
  "Version": 3,
  "ExportedAt": "2023-11-08T23:36:23.610Z",
  "ActionType": "Octopus.Script",
  "Author": "mcasperson",
  "Packages": [],
  "Parameters": [
    {
      "Id": "070f2882-2911-4297-b9e3-2da81abf6e70",
      "Name": "SerializeProject.Exported.Project.IgnoreAllChanges",
      "Label": "Ignore All Changes",
      "HelpText": "Selecting this option creates a Terraform module with the \"lifecycle.ignore_changes\" option set to \"all\". This allows the resources to be created if they do not exist, but won't update them if the module is reapplied.",
      "DefaultValue": "False",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    },
    {
      "Id": "4cb3da75-7449-4adb-b81a-e87dff371a27",
      "Name": "SerializeProject.ThisInstance.Terraform.Backend",
      "Label": "Terraform Backend",
      "HelpText": "The [backed](https://developer.hashicorp.com/terraform/language/settings/backends/configuration) to define in the Terraform module.",
      "DefaultValue": "s3",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "aa3df492-845a-4889-a7fc-c9c6f3a95a30",
      "Name": "SerializeProject.ThisInstance.Server.Url",
      "Label": "Octopus Server URL",
      "HelpText": "The URL of the Octopus Server hosting the project to be serialized.",
      "DefaultValue": "#{Octopus.Web.ServerUri}",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "e92dbdff-dd5a-4c95-91a1-40c0ccbb3b3f",
      "Name": "SerializeProject.ThisInstance.Api.Key",
      "Label": "Octopus API Key",
      "HelpText": "The Octopus API Key",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Sensitive"
      }
    },
    {
      "Id": "c906ecbd-f304-48b8-83ea-fe75008c37df",
      "Name": "SerializeProject.Exported.Space.Id",
      "Label": "Octopus Space ID",
      "HelpText": "The Space ID containing the project to be exported",
      "DefaultValue": "#{Octopus.Space.Id}",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "fae1f2e4-9be5-4380-9fd3-409a1a538b37",
      "Name": "SerializeProject.Exported.Project.Name",
      "Label": "Octopus Project Name",
      "HelpText": "The name of the project containing the runbook.",
      "DefaultValue": "#{Octopus.Project.Name}",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "a711f201-fa2f-4b32-9205-13f396c253d7",
      "Name": "SerializeProject.Exported.Runbook.Name",
      "Label": "Octopus Runbook Name",
      "HelpText": "The name of the runbook to serialize.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "27b222da-690c-4da3-8c60-d06bc7d3505b",
      "Name": "SerializeProject.Octopus.UploadSpace.Id",
      "Label": "Octopus Upload Space ID",
      "HelpText": "The ID of the space to upload the Terraform package to. Leave this blank to upload to the space defined in the `Octopus Space ID` parameter.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    }
  ],
  "Properties": {
    "Octopus.Action.RunOnServer": "true",
    "Octopus.Action.Script.ScriptBody": "import argparse\nimport os\nimport re\nimport socket\nimport subprocess\nimport sys\nfrom datetime import datetime\nfrom urllib.parse import urlparse\nfrom itertools import chain\nimport platform\nfrom urllib.request import urlretrieve\nimport zipfile\n\n# If this script is not being run as part of an Octopus step, return variables from environment variables.\n# Periods are replaced with underscores, and the variable name is converted to uppercase\nif \"get_octopusvariable\" not in globals():\n    def get_octopusvariable(variable):\n        return os.environ[re.sub('\\\\.', '_', variable.upper())]\n\n# If this script is not being run as part of an Octopus step, print directly to std out.\nif \"printverbose\" not in globals():\n    def printverbose(msg):\n        print(msg)\n\n\ndef printverbose_noansi(output):\n    \"\"\"\n    Strip ANSI color codes and print the output as verbose\n    :param output: The output to print\n    \"\"\"\n    output_no_ansi = re.sub('\\x1b\\[[0-9;]*m', '', output)\n    printverbose(output_no_ansi)\n\n\ndef get_octopusvariable_quiet(variable):\n    \"\"\"\n    Gets an octopus variable, or an empty string if it does not exist.\n    :param variable: The variable name\n    :return: The variable value, or an empty string if the variable does not exist\n    \"\"\"\n    try:\n        return get_octopusvariable(variable)\n    except:\n        return ''\n\n\ndef execute(args, cwd=None, env=None, print_args=None, print_output=printverbose_noansi):\n    \"\"\"\n        The execute method provides the ability to execute external processes while capturing and returning the\n        output to std err and std out and exit code.\n    \"\"\"\n    process = subprocess.Popen(args,\n                               stdout=subprocess.PIPE,\n                               stderr=subprocess.PIPE,\n                               text=True,\n                               cwd=cwd,\n                               env=env)\n    stdout, stderr = process.communicate()\n    retcode = process.returncode\n\n    if print_args is not None:\n        print_output(' '.join(args))\n\n    if print_output is not None:\n        print_output(stdout)\n        print_output(stderr)\n\n    return stdout, stderr, retcode\n\n\ndef is_windows():\n    return platform.system() == 'Windows'\n\n\ndef init_argparse():\n    parser = argparse.ArgumentParser(\n        usage='%(prog)s [OPTION] [FILE]...',\n        description='Serialize an Octopus project to a Terraform module'\n    )\n    parser.add_argument('--ignore-all-changes',\n                        action='store',\n                        default=get_octopusvariable_quiet(\n                            'SerializeProject.Exported.Project.IgnoreAllChanges') or get_octopusvariable_quiet(\n                            'Exported.Project.IgnoreAllChanges') or 'false',\n                        help='Set to true to set the \"lifecycle.ignore_changes\" ' +\n                             'setting on each exported resource to \"all\"')\n    parser.add_argument('--terraform-backend',\n                        action='store',\n                        default=get_octopusvariable_quiet(\n                            'SerializeProject.ThisInstance.Terraform.Backend') or get_octopusvariable_quiet(\n                            'ThisInstance.Terraform.Backend') or 'pg',\n                        help='Set this to the name of the Terraform backend to be included in the generated module.')\n    parser.add_argument('--server-url',\n                        action='store',\n                        default=get_octopusvariable_quiet(\n                            'SerializeProject.ThisInstance.Server.Url') or get_octopusvariable_quiet(\n                            'ThisInstance.Server.Url'),\n                        help='Sets the server URL that holds the project to be serialized.')\n    parser.add_argument('--api-key',\n                        action='store',\n                        default=get_octopusvariable_quiet(\n                            'SerializeProject.ThisInstance.Api.Key') or get_octopusvariable_quiet(\n                            'ThisInstance.Api.Key'),\n                        help='Sets the Octopus API key.')\n    parser.add_argument('--space-id',\n                        action='store',\n                        default=get_octopusvariable_quiet(\n                            'SerializeProject.Exported.Space.Id') or get_octopusvariable_quiet(\n                            'Exported.Space.Id') or get_octopusvariable_quiet('Octopus.Space.Id'),\n                        help='Set this to the space ID containing the project to be serialized.')\n    parser.add_argument('--project-name',\n                        action='store',\n                        default=get_octopusvariable_quiet(\n                            'SerializeProject.Exported.Project.Name') or get_octopusvariable_quiet(\n                            'Exported.Project.Name') or get_octopusvariable_quiet(\n                            'Octopus.Project.Name'),\n                        help='Set this to the name of the project to be serialized.')\n    parser.add_argument('--runbook-name',\n                        action='store',\n                        default=get_octopusvariable_quiet(\n                            'SerializeProject.Exported.Runbook.Name') or get_octopusvariable_quiet(\n                            'Exported.Runbook.Name'),\n                        help='Set this to the name of the project to be serialized.')\n    parser.add_argument('--upload-space-id',\n                        action='store',\n                        default=get_octopusvariable_quiet(\n                            'SerializeProject.Octopus.UploadSpace.Id') or get_octopusvariable_quiet(\n                            'Octopus.UploadSpace.Id') or get_octopusvariable_quiet('Octopus.Space.Id'),\n                        help='Set this to the space ID of the Octopus space where ' +\n                             'the resulting package will be uploaded to.')\n\n    return parser.parse_known_args()\n\n\ndef ensure_octo_cli_exists():\n    if is_windows():\n        print(\"Checking for the Octopus CLI\")\n        try:\n            stdout, _, exit_code = execute(['octo', 'help'])\n            printverbose(stdout)\n            if not exit_code == 0:\n                raise \"Octo CLI not found\"\n        except:\n            print(\"Downloading the Octopus CLI\")\n            urlretrieve('https://download.octopusdeploy.com/octopus-tools/9.0.0/OctopusTools.9.0.0.win-x64.zip',\n                        'OctopusTools.zip')\n            with zipfile.ZipFile('OctopusTools.zip', 'r') as zip_ref:\n                zip_ref.extractall(os.getcwd())\n\n\ndef check_docker_exists():\n    try:\n        stdout, _, exit_code = execute(['docker', 'version'])\n        printverbose(stdout)\n        if not exit_code == 0:\n            raise \"Docker not found\"\n    except:\n        print('Docker must be installed: https://docs.docker.com/get-docker/')\n        sys.exit(1)\n\n\ncheck_docker_exists()\nensure_octo_cli_exists()\nparser, _ = init_argparse()\n\n# Variable precondition checks\nif len(parser.server_url) == 0:\n    print(\"--server-url, ThisInstance.Server.Url, or SerializeProject.ThisInstance.Server.Url must be defined\")\n    sys.exit(1)\n\nif len(parser.api_key) == 0:\n    print(\"--api-key, ThisInstance.Api.Key, or ThisInstance.Api.Key must be defined\")\n    sys.exit(1)\n    \noctoterra_image = 'ghcr.io/octopussolutionsengineering/octoterra-windows' if is_windows() else 'ghcr.io/octopussolutionsengineering/octoterra'\noctoterra_mount = 'C:/export' if is_windows() else '/export'  \n\nprint(\"Pulling the Docker images\")\nexecute(['docker', 'pull', octoterra_image])\n\nif not is_windows():\n    execute(['docker', 'pull', 'ghcr.io/octopusdeploylabs/octo'])\n\n# Find out the IP address of the Octopus container\nparsed_url = urlparse(parser.server_url)\noctopus = socket.getaddrinfo(parsed_url.hostname, '80')[0][4][0]\n\nprint(\"Octopus hostname: \" + parsed_url.hostname)\nprint(\"Octopus IP: \" + octopus.strip())\n\nos.mkdir(os.getcwd() + '/export')\n\nexport_args = ['docker', 'run',\n               '--rm',\n               '--add-host=' + parsed_url.hostname + ':' + octopus.strip(),\n               '-v', os.getcwd() + '/export:' + octoterra_mount,\n               octoterra_image,\n               # the url of the instance\n               '-url', parser.server_url,\n               # the api key used to access the instance\n               '-apiKey', parser.api_key,\n               # add a postgres backend to the generated modules\n               '-terraformBackend', parser.terraform_backend,\n               # dump the generated HCL to the console\n               '-console',\n               # dump the project from the current space\n               '-space', parser.space_id,\n               # the name of the project to serialize\n               '-projectName', parser.project_name,\n               # the name of the runbook to serialize\n               '-runbookName', parser.runbook_name,\n               # ignoreProjectChanges can be set to ignore all changes to the project, variables, runbooks etc\n               '-ignoreProjectChanges=' + parser.ignore_all_changes,\n               # for any secret variables, add a default value set to the octostache value of the variable\n               # e.g. a secret variable called \"database\" has a default value of \"#{database}\"\n               '-defaultSecretVariableValues',\n               # detach any step templates, allowing the exported project to be used in a new space\n               '-detachProjectTemplates',\n               # Capture the octopus endpoint, space ID, and space name as output vars. This is useful when\n               # querying th Terraform state file to know which space and instance the resources were\n               # created in. The scripts used to update downstream projects in bulk work by querying the\n               # Terraform state, finding all the downstream projects, and using the space name to only process\n               # resources that match the current tenant (because space names and tenant names are the same).\n               # The output variables added by this option are octopus_server, octopus_space_id, and\n               # octopus_space_name.\n               '-includeOctopusOutputVars',\n               # Where steps do not explicitly define a worker pool and reference the default one, this\n               # option explicitly exports the default worker pool by name. This means if two spaces have\n               # different default pools, the exported project still uses the pool that the original project\n               # used.\n               '-lookUpDefaultWorkerPools',\n               # These tenants are linked to the project to support some management runbooks, but should not\n               # be exported\n               '-excludeAllTenants',\n               # The directory where the exported files will be saved\n               '-dest', octoterra_mount]\n\nprint(\"Exporting Terraform module\")\n_, _, octoterra_exit = execute(export_args)\n\nif not octoterra_exit == 0:\n    print(\"Octoterra failed. Please check the logs for more information.\")\n    sys.exit(1)\n\ndate = datetime.now().strftime('%Y.%m.%d.%H%M%S')\n\nprint(\"Creating Terraform module package\")\nif is_windows():\n    execute(['octo',\n             'pack',\n             '--format', 'zip',\n             '--id', re.sub('[^0-9a-zA-Z]', '_', parser.project_name + \"_\" + parser.runbook_name),\n             '--version', date,\n             '--basePath', os.getcwd() + '\\\\export',\n             '--outFolder', 'C:\\\\export'])\nelse:\n    _, _, _ = execute(['docker', 'run',\n                            '--rm',\n                            '--add-host=' + parsed_url.hostname + ':' + octopus.strip(),\n                            '-v', os.getcwd() + \"/export:/export\",\n                            'ghcr.io/octopusdeploylabs/octo',\n                            'pack',\n                            '--format', 'zip',\n                            '--id', re.sub('[^0-9a-zA-Z]', '_', parser.project_name + \"_\" + parser.runbook_name),\n                            '--version', date,\n                            '--basePath', '/export',\n                            '--outFolder', '/export'])\n\nprint(\"Uploading Terraform module package\")\nif is_windows():\n    _, _, _ = execute(['octo',\n                            'push',\n                            '--apiKey', parser.api_key,\n                            '--server', parser.server_url,\n                            '--space', parser.upload_space_id,\n                            '--package', 'C:\\\\export\\\\' +\n                            re.sub('[^0-9a-zA-Z]', '_', parser.project_name + \"_\" + parser.runbook_name) + '.' + date + '.zip',\n                            '--replace-existing'])\nelse:\n    _, _, _ = execute(['docker', 'run',\n                            '--rm',\n                            '--add-host=' + parsed_url.hostname + ':' + octopus.strip(),\n                            '-v', os.getcwd() + \"/export:/export\",\n                            'ghcr.io/octopusdeploylabs/octo',\n                            'push',\n                            '--apiKey', parser.api_key,\n                            '--server', parser.server_url,\n                            '--space', parser.upload_space_id,\n                            '--package', '/export/' +\n                            re.sub('[^0-9a-zA-Z]', '_', parser.project_name + \"_\" + parser.runbook_name) + '.' + date + '.zip',\n                            '--replace-existing'])\n\nprint(\"##octopus[stdout-default]\")\n\nprint(\"Done\")\n",
    "Octopus.Action.Script.ScriptSource": "Inline",
    "Octopus.Action.Script.Syntax": "Python"
  },
  "Category": "Octopus",
  "HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/octopus-serialize-runbook-to-terraform.json",
  "Website": "/step-templates/07b966c3-130c-4f13-ae0f-5105af5b97a1",
  "Logo": "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAC1QTFRFT6Tl////L5Pg8vj9Y67omsvwPJrisdfzfbzs5fL7y+T32Ov5isLucLXqvt31CJPHWwAABMJJREFUeNrs3deW4jAMAFDF3U75/89dlp0ZhiU4blJEjvQ8hYubLJsA00UCBCIQgQhEIAIRiEAEIhCBCEQgAhGIQAQiEIEIhD8kJm+t+QprfdKfB9HbYpx6CWfspj8HMi+gMgHL/AmQA8W3JTKH+ALFvzCeL0RbpyoCPE9IJeNOSQwh5Z3qd6yRGWQ2qi2cZQWxqj1WzQYSjeoJmJlAklOd4VlArOqPhQEkqBERToeMcfRJBkC0Uep8CfBpjz4JsHJ0zF3dkEWNje0kiB/sUC6eApndaIiCMyAa1PiwJ0AWhRGJHJJQHG2dC7h1rNbO1QOxSA7lNCkkKrQIpJCAB1GREILYIC1NAiwbpKFJgGWDNExcwGstfExcZBCHC6nOglshHtmhViLIig1RNBCN7qjtW8C0Z1UvJcC1Z9XmwMBzzvobmgAyEzgq91dtEEsBsQSQQAFZCSBAATEEEApHZbrVBIkkEIUPSVeB+KtALA0kXQUSrwKZBCIQBnk8Y4i5CsReBeKvkqLM+BCSDWJlrZFvGk9SRTHshkgjZCGAaArIxm3H3grhVzFlW2msfl1ca79UJ1bofYvsDHHlNdTZnlh5MghuPd5NdBDUNZHyCkfktIh03XzALGRPlBDPac7qgWjHZzWcmF5zmmkhidMQ6boKiDXcDTUEaylZqCGJ0Vjvu/fLJtHqhSANEvqb2OYqkOUqEHuVMbJcZdZCGiPhKhC4yjqiIjEE7XThMp8fAWII3mY3kUIQD+AMKQTzPiBhgQ63HlT/KSvgtoi0dq5mCPah1UIE0eh3sT0NhOByvKeAkFzi8PgQomumFhsyOxpIzZN4gLOj5plVwNpR0b2AuePWKBEHQu24pSsJA+LVCeHHQxZ1SiyDIdqok8IOhSSnTottHEQTdyt4ettAj4KkzA4dMikk2Dht2S5ptm1vswnPDxn0YyDZ5oDM3iToo2T5voWaYe+Q+vdjH80QyAzZhCgcDtLMI1Tmtz9w++XHgziHQHJJu/OZ3bs9Xn8gQ72NcP3dKqEfkp10F51xhoIi2I91R+LurXV/5q7pH+wx061CzO16oSQleMyr8fXvwMA0Pro8432DPD/ySx8XrHfSuDAM8n6UhnjQabaiXf5Bq/lREHvEeNtn1rJ08+C/uXkQZHeguxAPC3UvtcJYUogLzZX5hhZZvS6onG5lxXtzWGaygwb79vT/IXhdlNibwlKYOR6T8xjI7W8n+xV7T+GH4tMzWwR+lZhRkJYSsC0thpmCYqyngOz3rN2FLBZ2wZflBCggUHF0Vnp88JKienzIXLSEZCZqU7IKr/gQW9yx3pzV7Y9kvWZWTRRIqDmTtRUnU7b2lLcTYmoqHqnmiO1poER0SPkAeZMAZxaJx0Y3TCdAclsIqDz03ALcyxfTCZBsthoGXWmigGyVhWPLFJJfuuKQWycoEFdXbH4dJJoJxNR1eD/kshz6yn48cF8yW8sFoitflB1w6Q8n+/15Za7oA17/pYNmYgP5fmWm8L1NOHPWgK8kuFew1/JXtOA0yJCv7ah7X8ObUuT5kObU30+fDZm8+zqP+HTIpK0xQ796b5Kv2hSIQAQiEIEIRCACEYhABCIQgQhEIAIRiEAEIpBf8UeAAQAEjtYmlDTcCgAAAABJRU5ErkJggg==",
  "$Meta": {
    "Type": "ActionTemplate"
  }
}

History

Page updated on Wednesday, November 8, 2023