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 callsnew 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 withpulumi config set --secret consolePassword <value>and read in the program withconfig.requireSecret("consolePassword"). Stack state lives inPulumi.<stack>.yamlfiles, which are gitignored so secrets never enter git. - Stacks.
pulumi stack init devcreates 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 thetoAccountId 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 theaccountIdon GPU functions) are typed as numbers in the SDKs, because the upstream API sends numbers.
id into a number typed input is a compile error in every typed language, so each language has its own conversion:
- TypeScript
- Python
- Go
- .NET
- Java
- YAML
GpuKeypairexposes nokeypairIdproperty, onlyid, which already is the keypair id. The GPU examples convertkeypair.idwith the same helper and carry a comment explaining why, matching the Pulumi GPU reference.- Object Storage’s
accountIdfields are string typed, so no conversion happens anywhere inobject-storage/or the storage part ofcomplete/. - YAML uses
fn::toNumber, the built in function, instead of a helper.
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:
AppStackArgsis a plain interface listing the component’s inputs. Every field is typedpulumi.Input<...>, which means a caller may pass either a plain value or a live output from another resource.AppStackextendspulumi.ComponentResource, registered with the type string"biznetgio-example:index:AppStack"in itssuper(...)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 makespulumi destroytear the whole stack down in the right order. - Outputs are assigned to public fields, then published with
this.registerOutputs(...), which makes them appear inpulumi stack output.
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’sgo.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:- Copy one existing language folder’s six examples and rewrite the programs in the new language, keeping every comment.
- Pin the SDK and provider versions in that language’s dependency file.
- Add the language to the CI
languageinput options, thediscoverjob’slangslist, and a new build leg with the right setup action.
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.