Skip to main content
This is the deep dive into biznetgio-example-pulumi. It assumes you have read What is Infrastructure as Code? or know what a Pulumi stack is. The Pulumi reference pages document every resource used here.

What a Pulumi program is, in one paragraph

A Pulumi program is a real program in a real language, not a markup file. You write ordinary code that calls new biznetgio.NeoliteVm(...) the way you would call any constructor, and Pulumi’s engine records every resource in a graph instead of executing the constructor’s side effects. References between resources, like keypair.keypairId passed into the VM, become edges in that graph, so the engine knows to create the keypair before the VM. The program then declares outputs, and pulumi up drives the graph against the Biznet GIO API. The same program structure exists in all six languages because it is the same engine underneath.

A project’s anatomy

Each folder is an independent Pulumi project. Pulumi.yaml declares the identity:
runtime picks the language engine. Around that file each language brings its own files, listed in How the repos are organized. Two concepts appear in every language’s program:
  • Config. The stack’s settings, like consolePassword, set with pulumi config set --secret consolePassword <value> and read in the program with config.requireSecret("consolePassword"). Stack state lives in Pulumi.<stack>.yaml files, which are gitignored so secrets never enter git.
  • Stacks. pulumi stack init dev creates an independent deployment target per folder. One example’s stack can never touch another’s.

The id conversion convention

The single most important convention in this repo is the toAccountId helper, and it exists because of a real type mismatch between Pulumi and the upstream API:
  • Pulumi resource ids are always strings.
  • Several Biznet GIO inputs that accept those ids (neoliteAccountId, snapshotId, metalAccountId, additionalIpId, and the accountId on GPU functions) are typed as numbers in the SDKs, because the upstream API sends numbers.
Passing a string id into a number typed input is a compile error in every typed language, so each language has its own conversion:
Three exceptions to remember:
  • GpuKeypair exposes no keypairId property, only id, which already is the keypair id. The GPU examples convert keypair.id with the same helper and carry a comment explaining why, matching the Pulumi GPU reference.
  • Object Storage’s accountId fields are string typed, so no conversion happens anywhere in object-storage/ or the storage part of complete/.
  • YAML uses fn::toNumber, the built in function, instead of a helper.
Both quirks were found by compiling this repo against the real SDKs, and the docs site was updated to match. When you add code that passes an id anywhere, check which type the input wants before writing the reference.

The component in complete/

complete/ bundles “one app” (NEO Lite Pro VM plus Object Storage bucket and credential) into a reusable component, the Pulumi equivalent of the Terraform module. The TypeScript version is appStack.ts:
  • AppStackArgs is a plain interface listing the component’s inputs. Every field is typed pulumi.Input<...>, which means a caller may pass either a plain value or a live output from another resource.
  • AppStack extends pulumi.ComponentResource, registered with the type string "biznetgio-example:index:AppStack" in its super(...) call.
  • Each child resource is created with { parent: this } in its options. That one line puts every child in a tree under the component, which is what makes pulumi destroy tear the whole stack down in the right order.
  • Outputs are assigned to public fields, then published with this.registerOutputs(...), which makes them appear in pulumi stack output.
The same pattern exists in every code language, with language appropriate naming: YAML cannot define components, a language limitation, so yaml/complete/ shows the same five resources flat, with a comment explaining the difference. The modules and components tutorial teaches the pattern itself.

Version pinning and the Go detail

Every language pins the SDK and provider versions it was built against, listed in the table in Conventions. One pin needs special care: the Go SDK’s go.mod requires a recent toolchain, which is why the Go folders declare go 1.25.11 and the CI installs 1.25.x. The workflow comment says to keep it in lockstep with the provider repo. If Go updates ever break a build, that pin is the first thing to check.

Adding a new example

The steps mirror the Terraform ones, plus a language matrix:
1

Copy the closest example in the same language

Copy the folder for the closest product line. Keep the Pulumi.yaml name and description format, biznetgio-example-<example>.
2

Rewrite the program for the new shape

Keep the bilingual banner and comments, the toAccountId helper where needed, and the cost safety defaults (payWithCreditCard defaults to false, one-shot options commented out).
3

Build it

Run the language’s build command from Local setup. Fix type errors until it compiles; the type checker will catch every id conversion you missed.
4

Mirror it to the other five languages

Each example must exist in every language, so the contribution is six programs. Translate the TypeScript structure, not the comments; the comments stay the same bilingual format in every language. YAML gets the flat version of whatever the component would be.
5

Update READMEs and CI

Update the root README table and the per-folder READMEs, and register the new example in the example input options and the discover job list in the workflow, as Pipelines describes.
6

Open the PR

Fill the PR template checklist, which asks exactly these questions.

Adding a whole new language

Pulumi supports more runtimes than the six here. Adding one means:
  1. Copy one existing language folder’s six examples and rewrite the programs in the new language, keeping every comment.
  2. Pin the SDK and provider versions in that language’s dependency file.
  3. Add the language to the CI language input options, the discover job’s langs list, and a new build leg with the right setup action.
Discuss the language in a feature request first. A new language is a big mirroring commitment, since every future example change must be translated six or seven times.

Running an example, for reference

preview is the plan; up is the apply. The full user facing walkthrough is the Pulumi quickstart, and the examples page lists what each folder covers. For the exhaustive file-by-file reference, including the exact conversion code in all six languages and the complete inventory of number-typed inputs, see the Complete code walkthrough.