Pulumi onboarding
This topic describes how to onboard existing resources into a Pulumi project.
Note: The onboarding. enhancement is available from version 26.4 and later
Overview
The Pulumi onboarding process is designed for a clean stack. It discovers existing resources from a Connect server and imports them into your Pulumi project, generating the corresponding infrastructure code automatically.
For a video illustrating Pulumi onboarding with AI-assisted code refactoring, see the Connect video gallery.
The steps for onboarding resources are described below:
Step 1: Locate the onboarding tools
Step 2: Set required environment variables
Step 3: Run the onboarding scripts
Prerequisites
This section describes the prerequisites for running the onboarding scripts.
To prepare your system for the onboarding scripts:
-
Install the Connect Pulumi provider plugin. For details, see Pulumi installation and upgrade.
-
Set up your Pulumi project. Use the workflow that matches your project type:
Step 1: Locate the onboarding tools
The onboarding tools are installed in the Utilities directory of your Connect installation:
-
mfcOnboardingPython.shfor Python projects. -
mfcOnboardingYaml.shfor YAML projects. -
mfcResourceDiscovery.shfor discovering existing resource IDs from the Connect server.
Run these tools directly from the installation directory and pass the path to your Pulumi project as an argument. Do not copy the tools into your Pulumi project. The scripts automatically locate your Connect installation.
Step 2: Set required environment variables
All onboarding scripts require the following environment variables:
| Variable | Required | Description |
|---|---|---|
MFC_CONNECT_URL
|
Yes | URL of your Connect server, for example http://localhost:8081. |
MFC_ACCESS_TOKEN
|
Yes | Your Connect access token. For details, see Manage access tokens. |
PULUMI_STACK
|
Yes | Name of the Pulumi stack to target. |
PULUMI_CONFIG_PASSPHRASE
|
No | Passphrase used to encrypt and decrypt stack secrets when using a local backend (pulumi login --local). Set this variable to run non-interactively. If omitted, Pulumi prompts for the passphrase during the run. |
Step 3: Run the onboarding scripts
Run the script that matches your project type and pass your Pulumi project directory as an argument.
For YAML projects:
MFC_CONNECT_URL=<connect-url> MFC_ACCESS_TOKEN=<your-access-token> PULUMI_STACK=<your-stack> /opt/MicroFocusConnect/Utilities/mfcOnboardingYaml.sh <your-pulumi-project-dir>
For Python projects:
MFC_CONNECT_URL=<connect-url> MFC_ACCESS_TOKEN=<your-access-token> PULUMI_STACK=<your-stack> /opt/MicroFocusConnect/Utilities/mfcOnboardingPython.sh <your-pulumi-project-dir>
If your stack uses secrets, include PULUMI_CONFIG_PASSPHRASE. Otherwise, Pulumi prompts for it:
MFC_CONNECT_URL=<connect-url> MFC_ACCESS_TOKEN=<your-access-token> PULUMI_STACK=<your-stack> PULUMI_CONFIG_PASSPHRASE=<your-passphrase> /opt/MicroFocusConnect/Utilities/mfcOnboardingPython.sh <your-pulumi-project-dir>
Note: Place your Pulumi project under Git version control immediately after onboarding, and before making additional changes. This is important especially before using an AI assistant to refactor generated code into strongly typed objects. This creates a known checkpoint so you can review each change with git diff, and revert if a later manual or AI-assisted edit breaks behavior or drifts from the Connect configuration.
Refactor generated Python code
By default, pulumi import generates Python code using dictionary-style arguments for nested properties. While this works, it does not provide the same IDE autocompletion, inline help, and type safety as strongly typed inputs.
We recommend converting generated code to use the strongly typed *Args classes provided by the Connect Python SDK. For available resource types and related classes, see Pulumi integration.
You can use an AI coding assistant to perform this conversion automatically with the following prompt.
AI refactor prompt
Refactor this Python Pulumi project.
CONTEXT
- Entrypoint: __main__.py, currently builds all resources using plain dicts.
- Provider SDK: pulumi_mfcpulumiprovider, located at
./python-sdk/python/pulumi_mfcpulumiprovider/.
Read _inputs.py (nested input types) and the per-resource modules (top-level
*Args) and treat them as the ONLY source of truth for class names and fields.
TASK
1. Typed args: replace every dict with the matching typed *Args class from the
SDK -- top-level resource args and all nested types (field mappings,
conditions, calculated values, filters, maps, etc.).
- EXCEPTION: fields declared as free-form string maps (Mapping[str, str]),
e.g. datasource_properties and secret_datasource_properties, stay as
plain dicts.
- If no matching *Args class exists, LEAVE THE DICT AS-IS and list it in the
final summary. Never invent class names or field names.
2. Convert inline and one-to-one. No shared helpers, loops, constants, or
reusable configs -- one dict in, one typed object out, in the same position.
3. File layout -- one resource per file:
resources/__init__.py
resources/datasources/__init__.py
resources/datasources/<variable_name>.py
resources/usermaps/__init__.py
resources/usermaps/<variable_name>.py
resources/logicalgroups/__init__.py
resources/logicalgroups/<variable_name>.py
resources/connections/__init__.py
resources/connections/<variable_name>.py
File name = the Python variable currently holding the resource, verbatim
(alm_datasource -> resources/datasources/alm_datasource.py).
Each file constructs exactly one resource and assigns it to a module-level
variable with the SAME name as in the original __main__.py.
4. Explicit re-exports -- package __init__.py files are the public surface.
No dynamic discovery: no pkgutil, no importlib, no globals() manipulation.
Every resource is listed by hand so the inventory is greppable and reviewable.
Each subpackage __init__.py imports its own resource modules, alphabetically,
and declares __all__. Example, resources/datasources/__init__.py:
from resources.datasources.alm_datasource import alm_datasource
from resources.datasources.crm_datasource import crm_datasource
__all__ = ["alm_datasource", "crm_datasource"]
resources/__init__.py aggregates the four subpackages and builds __all__ by
concatenating theirs -- do not re-list individual names at the root:
from resources import datasources, logicalgroups, usermaps, connections
from resources.datasources import *
from resources.logicalgroups import *
from resources.usermaps import *
from resources.connections import *
__all__ = [
*datasources.__all__,
*logicalgroups.__all__,
*usermaps.__all__,
*connections.__all__,
]
Importing a resource module is what registers the resource with Pulumi -- the
constructor runs at module top level. A module not reachable from
resources/__init__.py is a resource that does not exist in the stack.
A resource is CONSTRUCTED once, in its own module; the imports above only
bind names to that same object and do not create duplicates.
5. __main__.py becomes imports + exports ONLY -- the one place that imports from
the package rather than from modules:
import pulumi
from resources import * # noqa: F401,F403
INVARIANTS (must hold exactly)
- Every Pulumi logical name (1st constructor arg) is byte-identical.
- Every property value is identical; execution_order values unchanged;
list order in mappings/conditions/filters unchanged.
- No resources added, removed, or renamed.
- No new dependencies between resources.
- Every resource in the original __main__.py has exactly one file and exactly
one entry in its subpackage __all__.
Manually import resources
This section describes how to handle advanced cases not covered by the onboarding scripts.
To import resources manually:
-
Generate the resource ID file:
Copy codemfcResourceDiscovery.sh -discoverResources --connect-url <connect-url> --access-token <your-access-token> --out resources.json -
Import with Pulumi:
Copy code# Python projects
pulumi --stack <your-stack> import --file resources.json --out __main__.py --skip-preview
# YAML projects
pulumi --stack <your-stack> import --file resources.json --out imported.yaml --skip-previewNote: For YAML projects, merge
imported.yamlintoPulumi.yamlunderresources.

