NETSetupConfig
public class NETSetupConfig : NETSetupEntityBase
1. Constructors
1.1. NETSetupConfig(ConfigName, Locale, Timezone, DirectoryService, NamedItemKeyedCollection<SoftwareName, Software>, NamedItemKeyedCollection<PersonName, Contact>, int, IP, Uri, Uri, Uri)
[JsonConstructor]
public NETSetupConfig(ConfigName configName, Locale locale, Timezone timezone, DirectoryService directoryService, NamedItemKeyedCollection<SoftwareName, Software> softwareList, NamedItemKeyedCollection<PersonName, Contact> contacts, int subnet, IP dhcpIP, Uri configRepo1, Uri configRepo2, Uri configRepo3)
| Name | Type | Description |
|---|---|---|
configName |
ConfigName |
|
locale |
Locale |
|
timezone |
Timezone |
|
directoryService |
DirectoryService |
|
softwareList |
NamedItemKeyedCollection<SoftwareName, Software> |
|
contacts |
NamedItemKeyedCollection<PersonName, Contact> |
|
subnet |
int |
|
dhcpIP |
IP |
|
configRepo1 |
Uri |
|
configRepo2 |
Uri |
|
configRepo3 |
Uri |
2. Properties
2.1. ConfigName
public ConfigName ConfigName { get; }
2.2. ConfigRepo1
public Uri ConfigRepo1 { get; set; }
2.3. ConfigRepo2
public Uri ConfigRepo2 { get; set; }
2.4. ConfigRepo3
public Uri ConfigRepo3 { get; set; }
2.5. Contacts
public NamedItemKeyedCollection<PersonName, Contact> Contacts { get; }
2.6. CustomerDomain
Optional explicit customer mail/dns domain (e.g. "test01.iam.free"). When null, NixMail/NixDns templates fall back to DirectoryService.Name.Value.
public string CustomerDomain { get; set; }
2.7. DhcpIP
public IP DhcpIP { get; set; }
2.8. DirectoryService
public DirectoryService DirectoryService { get; set; }
2.9. DnsProvider
DNS / ACME provider configuration consumed by the dns VM (lego DNS-01 + ddns-updater). Required for NixDns template emit.
public DnsProviderOptions DnsProvider { get; set; }
2.10. DomainProvider
Dyndns provider (non-secret knobs) for the dns VM’s ddns-updater. Set by company.AddDomainProvider(...) via the translator, which ALSO inserts a DomainProvider-tagged User carrying the secret. Independent of DnsProvider (dyndns works with zero ACME). Null ⇒ no dyndns.
public DomainProviderOptions DomainProvider { get; set; }
2.11. GatewayIP
Upstream/default gateway IP that the Proxmox host + customer VMs route through. Distinct from any IRouter device in the directory (a router/firewall device represents the firewall as a network role, not necessarily the gateway IP). Phase 1: this points at the actual upstream ISP/DC port (or test-bench upstream). Phase 2 (future): once OPNsense firewall is provisioned + reachable on its LAN IP, a follow-up step rewrites host + VM gateway entries to that LAN IP. See docs/firewall.adoc for the takeover plan. When IP.None, callers fall back to the legacy Get<IRouter>().IP behavior so pre-existing customer JSONs (which don’t carry the field yet) keep working.
public IP GatewayIP { get; set; }
2.12. IpfsCluster
Per-customer private-IPFS-cluster secrets (one swarm key + one cluster secret shared by every NixIPFS host). Generated once by IpfsClusterSecretsGenerator when the config declares a NixIPFS host; persisted so re-runs keep the same swarm. Null when no IPFS host is configured.
public IpfsClusterOptions IpfsCluster { get; set; }
2.13. IsTestBench
Test-bench-only flag (NOT persisted to JSON). When true, VM provisioning paths (NixTemplateFactory static-IP emission, EnsureLanBridgeVmbr1, OpnsenseInstall) short-circuit so the Hyper-V test bench can run on external DHCP via vmbr0 only. Production code paths always treat it as false.
[JsonIgnore]
public bool IsTestBench { get; set; }
2.14. JsonSerializerOptions
public static JsonSerializerOptions JsonSerializerOptions { get; }
2.15. Locale
public Locale Locale { get; set; }
2.16. MailHostname
Optional first-segment hostname for the customer’s mail server (the label before the customer domain in <label>.<CustomerDomain>). When null, defaults to "mail". Drives the A record + MX RR target emitted by NixDnsTemplate. Translator seeds this from the implicit Liberator Mail VM (machine name "Mail" lowercased).
public string MailHostname { get; set; }
2.17. MailRelay
Optional smarthost relay for the mail VM postfix submission path. When null, postfix does direct MX lookup (no relayhost emitted).
public MailRelayOptions MailRelay { get; set; }
2.18. OuSubnets
Per-OU LAN subnet (CIDR) carved by LiberatorWithWinDCTranslator's internal NetworkPlan when allocating device IPs. Key = OU name (plain string, must match OrganizationalUnit.Name.Value), value = CIDR (e.g. "10.10.10.0/24"). Devices in an OU absent from this map fall back to the default OU’s range. Gateway is global (GatewayIP), not per OU.
public Dictionary<string, string> OuSubnets { get; set; }
2.19. SelectedHostname
When set, the user explicitly selected this hostname during config resolution. Used by IdentifyHost to match by hostname instead of serial number.
[JsonIgnore]
public string SelectedHostname { get; set; }
2.20. SoftwareList
public NamedItemKeyedCollection<SoftwareName, Software> SoftwareList { get; }
2.21. Subnet
public int Subnet { get; set; }
2.22. Timezone
public Timezone Timezone { get; set; }
2.23. UseReusableIso
Opt-in switch for the reusable-ISO provisioning path (persisted to JSON so the deployed config on the Proxmox host carries it). When true, every Windows VM boots the single shared NETSetup.iso on iso storage (no per-VM offline ISO build) and its NIC MAC + serial are pinned deterministically from the vmid via ProxmoxNodeIdentity, so the guest’s computed serial matches its online Config/*.map key. Default false keeps the per-VM offline ISO build (baked config) as the DEFAULT.
public bool UseReusableIso { get; set; }
2.24. WindowsCapabilities
Per-machine Windows post-install capability opt-ins, keyed by machine name (plain string, must match Machine.MachineName.Value, matched case-insensitively by ConfigExtensions.GetWindowsCapabilities). Machines absent from this map get nothing extra, so every existing customer config keeps its current behaviour.
This is the config-driven answer to "the node installed, but it still cannot do X": WinRM, the WSL2 runtime, local-group membership and the join OU are declared HERE rather than fixed by hand on the node afterwards. Applied by WindowsCapabilitiesApplier.
public Dictionary<string, WindowsCapabilities> WindowsCapabilities { get; set; }
3. Methods
3.1. AreNamesValid()
public (bool, string) AreNamesValid()
Returns: (bool, string)
3.2. CreateBuilder(string)
public static NETSetupConfigBuilder CreateBuilder(string configName)
| Name | Type | Description |
|---|---|---|
configName |
string |
Returns: NETSetupConfigBuilder
3.3. CreateCollection2<TItemKey, TItem>(IEnumerable<TItem>)
protected static NamedItemKeyedCollection<TItemKey, TItem> CreateCollection2<TItemKey, TItem>(IEnumerable<TItem> items = null)
where TItemKey : IComparable<TItemKey> where TItem : IKeyed<TItemKey>
| Name | Type | Description |
|---|---|---|
items |
IEnumerable<TItem> |
Returns: NamedItemKeyedCollection<TItemKey, TItem>
3.4. CreateCollection<TItemKey, TItem, TInterface>()
protected static KeyedCollectionInterfaceAdapter<IKeyedCollection<TItemKey, TItem>, TItemKey, TItem, TInterface> CreateCollection<TItemKey, TItem, TInterface>()
where TItemKey : IComparable<TItemKey> where TItem : NETSetupEntityBase, TInterface where TInterface : IKeyed<TItemKey>
Returns: KeyedCollectionInterfaceAdapter<IKeyedCollection<TItemKey, TItem>, TItemKey, TItem, TInterface>
3.5. CreateCollection<TItemKey, TItem, TInterface>(IEnumerable<TItem>)
protected static KeyedCollectionInterfaceAdapter<IKeyedCollection<TItemKey, TItem>, TItemKey, TItem, TInterface> CreateCollection<TItemKey, TItem, TInterface>(IEnumerable<TItem> items)
where TItemKey : IComparable<TItemKey> where TItem : NETSetupEntityBase, TInterface where TInterface : IKeyed<TItemKey>
| Name | Type | Description |
|---|---|---|
items |
IEnumerable<TItem> |
Returns: KeyedCollectionInterfaceAdapter<IKeyedCollection<TItemKey, TItem>, TItemKey, TItem, TInterface>
3.6. CreateCollection<TItemKey, TItem>(IEnumerable<TItem>)
protected static IKeyedCollection<TItemKey, TItem> CreateCollection<TItemKey, TItem>(IEnumerable<TItem> items = null)
where TItemKey : IComparable<TItemKey> where TItem : IKeyed<TItemKey>
| Name | Type | Description |
|---|---|---|
items |
IEnumerable<TItem> |
Returns: IKeyedCollection<TItemKey, TItem>
3.7. ThrowIfNamesAreNotValid()
[Obsolete("Only for old windows ad based systems")]
public void ThrowIfNamesAreNotValid()
4. Extension Methods
4.1. AddMailRelay(string, string, SecretString, int)
Declare an outbound SMTP smarthost. Mail VM postfix gets relayhost = [host]:port and SASL credentials (saslUser/saslPassword).
public static NETSetupConfig AddMailRelay(this NETSetupConfig config, string host, string saslUser, SecretString saslPassword, int port = 587)
| Name | Type | Description |
|---|---|---|
config |
NETSetupConfig |
Customer config the relay user is attached to. |
host |
string |
Smarthost FQDN (e.g. |
saslUser |
string |
SASL username (often a full email address). |
saslPassword |
SecretString |
SASL password. |
port |
int |
Submission port. Default 587. |
Returns: NETSetupConfig — The config (fluent).
4.2. AddMailRelayViaCert(string, int)
Declare an outbound smarthost authenticated by the mail VM’s TLS *client certificate* (no SASL) - the Microsoft 365 "inbound connector, certificate" method. Sets config.MailRelay directly (POCO path, no credential user) with UseClientCert = true. Requires a publicly-trusted cert on the mail VM (real Let’s Encrypt) whose subject matches the domain the M365 connector expects.
public static NETSetupConfig AddMailRelayViaCert(this NETSetupConfig config, string host, int port = 25)
| Name | Type | Description |
|---|---|---|
config |
NETSetupConfig |
Customer config. |
host |
string |
Connector endpoint FQDN (e.g. |
port |
int |
Relay port. Default 25 (connector). |
Returns: NETSetupConfig — The config (fluent).
4.3. GenerateDismUnattendedXMLString(Computer)
Generates a stripped unattend XML string for DISM-based installation. Uses templates that contain only specialize + oobeSystem passes (no windowsPE pass).
public static string GenerateDismUnattendedXMLString(this NETSetupConfig config, Computer computer)
| Name | Type | Description |
|---|---|---|
config |
NETSetupConfig |
|
computer |
Computer |
Returns: string
4.4. GenerateMapFiles(IFileOperator, string)
public static void GenerateMapFiles(this NETSetupConfig config, IFileOperator source, string identifier)
| Name | Type | Description |
|---|---|---|
config |
NETSetupConfig |
|
source |
IFileOperator |
|
identifier |
string |
4.5. GenerateMapFiles(int)
public static void GenerateMapFiles(this NETSetupConfig config, int ticketId)
| Name | Type | Description |
|---|---|---|
config |
NETSetupConfig |
|
ticketId |
int |
4.6. GenerateUnattendedFile(ISystemHost, IFileOperator, SubPath)
public static void GenerateUnattendedFile(this NETSetupConfig netSetupConfig, ISystemHost host, IFileOperator configOp, SubPath configSubPath)
| Name | Type | Description |
|---|---|---|
netSetupConfig |
NETSetupConfig |
|
host |
ISystemHost |
|
configOp |
IFileOperator |
|
configSubPath |
SubPath |
4.7. GenerateUnattendedXMLString(Computer)
public static string GenerateUnattendedXMLString(this NETSetupConfig config, Computer computer)
| Name | Type | Description |
|---|---|---|
config |
NETSetupConfig |
|
computer |
Computer |
Returns: string
4.8. GetComputer(MachineName)
public static Computer GetComputer(this NETSetupConfig this, MachineName name)
| Name | Type | Description |
|---|---|---|
this |
NETSetupConfig |
|
name |
MachineName |
Returns: Computer
4.9. GetComputer<T>(MachineName)
public static T GetComputer<T>(this NETSetupConfig netSetupConfig, MachineName name)
where T : Computer
| Name | Type | Description |
|---|---|---|
netSetupConfig |
NETSetupConfig |
|
name |
MachineName |
Returns: T
4.10. GetComputer<T>(SerialNumber)
public static T GetComputer<T>(this NETSetupConfig this, SerialNumber serialNumber)
where T : Computer
| Name | Type | Description |
|---|---|---|
this |
NETSetupConfig |
|
serialNumber |
SerialNumber |
Returns: T
4.11. GetFirstComputer<T>()
public static T GetFirstComputer<T>(this NETSetupConfig this) where T : Computer
| Name | Type | Description |
|---|---|---|
this |
NETSetupConfig |
Returns: T
4.12. GetFirstUser(Role)
public static User GetFirstUser(this NETSetupConfig this, Role role)
| Name | Type | Description |
|---|---|---|
this |
NETSetupConfig |
|
role |
Role |
Returns: User
4.13. GetGatewayRouter()
Legacy gateway-router fallback for configs predating GatewayIP. A Liberator config carries a MikroTik AP (NETSetupMikrotik : Router ⇒ also IRouter) next to the firewall/gateway router, so filter the L2 AP out to keep SingleOrDefault selecting exactly the gateway. Returns null when no gateway router exists - callers decide how to surface that (throw vs ExitFailure).
public static IRouter? GetGatewayRouter(this NETSetupConfig netSetupConfig)
| Name | Type | Description |
|---|---|---|
netSetupConfig |
NETSetupConfig |
Returns: IRouter?
4.14. GetSoftwareList(ILogger, Computer)
public static List<Software> GetSoftwareList(this NETSetupConfig netSetupConfig, ILogger logger, Computer computer)
| Name | Type | Description |
|---|---|---|
netSetupConfig |
NETSetupConfig |
|
logger |
ILogger |
|
computer |
Computer |
Returns: List<Software>
4.15. GetSoftwareList(ILogger, MachineName)
public static List<Software> GetSoftwareList(this NETSetupConfig netSetupConfig, ILogger logger, MachineName name)
| Name | Type | Description |
|---|---|---|
netSetupConfig |
NETSetupConfig |
|
logger |
ILogger |
|
name |
MachineName |
Returns: List<Software>
4.16. GetWindowsCapabilities(string)
Returns the WindowsCapabilities declared for machineName, or null when the config declares none. Machine names are matched case-insensitively, because a config is hand-written and Windows does not care about the casing of a hostname either.
public static WindowsCapabilities GetWindowsCapabilities(this NETSetupConfig config, string machineName)
| Name | Type | Description |
|---|---|---|
config |
NETSetupConfig |
|
machineName |
string |
Returns: WindowsCapabilities
4.17. IdentifyHost(ISystemHost)
public static Machine IdentifyHost(this NETSetupConfig netSetupConfig, ISystemHost host)
| Name | Type | Description |
|---|---|---|
netSetupConfig |
NETSetupConfig |
|
host |
ISystemHost |
Returns: Machine
4.18. MoveVm(string, SerialNumber, int?)
Re-pins an implicit Liberator VM (added by LiberatorWithWinDCTranslator) onto a different physical host. Use at customer.cs scope right after translator.Translate() when an existing prod VM lives on a slave Power Station instead of the Liberator (e.g. migrating Nextcloud disk-state to its current home). Rewrites the hosted-serial suffix only; OS, IP, users, and ports are preserved.
public static NETSetupConfig MoveVm(this NETSetupConfig config, string deviceName, SerialNumber newHostSerial, int? newVmId = null)
| Name | Type | Description |
|---|---|---|
config |
NETSetupConfig |
|
deviceName |
string |
|
newHostSerial |
SerialNumber |
|
newVmId |
int? |
Optional override for the VMID prefix; defaults to the current VMID. Use when the target host already runs something at the implicit VMID. |
Returns: NETSetupConfig
4.19. OverrideSerialNumber(ISystemHost, MachineName)
public static void OverrideSerialNumber(this NETSetupConfig netSetupConfig, ISystemHost host, MachineName name)
| Name | Type | Description |
|---|---|---|
netSetupConfig |
NETSetupConfig |
|
host |
ISystemHost |
|
name |
MachineName |
4.20. OverrideSerialNumber(ISystemHost, int)
public static void OverrideSerialNumber(this NETSetupConfig netSetupConfig, ISystemHost host, int index)
| Name | Type | Description |
|---|---|---|
netSetupConfig |
NETSetupConfig |
|
host |
ISystemHost |
|
index |
int |
4.21. OverrideSerialNumber(ISystemHost, string)
public static void OverrideSerialNumber(this NETSetupConfig netSetupConfig, ISystemHost host, string name)
| Name | Type | Description |
|---|---|---|
netSetupConfig |
NETSetupConfig |
|
host |
ISystemHost |
|
name |
string |
4.22. SetDisk(string, string)
Sets a machine’s system-disk size in the config so provisioning sizes the VM from the CONFIG instead of the factory fallback. The symmetric twin of SetMemory(NETSetupConfig, string, string), and the reason it exists: a build node whose Docker data alone measures ~48 GB cannot live on the 64 G default - devBuilder04 ran out at 18.3 GB free and no amount of software config could fix that.
IN-MEMORY ONLY - same constraint as SetMemory(NETSetupConfig, string, string). Machine.Hardware is typed IHardware and System.Text.Json cannot deserialize an interface, so a persisted config carrying Hardware throws "Deserialization of interface or abstract types is not supported" and every guest reading it fails. Call it on a config you are about to provision from, never on one you serialize.
public static NETSetupConfig SetDisk(this NETSetupConfig config, string machineName, string size)
| Name | Type | Description |
|---|---|---|
config |
NETSetupConfig |
|
machineName |
string |
Machine in the config to size, e.g. "devBuilder05". |
size |
string |
Disk size parsed by |
Returns: NETSetupConfig
4.23. SetMemory(string, string)
Sets a machine’s RAM in the config so provisioning sizes the VM from the CONFIG instead of the factory fallback (ProxmoxWindowsVmFactory only uses its own default when the machine declares no memory). Lives here because the concrete Hardware / RandomAccessModule types are in osisa.SystemManagement, which customer scripts cannot reference without tripping the CPM version pins.
IN-MEMORY ONLY - call it on a config you are about to provision from, NEVER on one you serialize. Machine.Hardware is typed IHardware and System.Text.Json cannot deserialize an interface, so a persisted config carrying Hardware throws "Deserialization of interface or abstract types is not supported" and every guest reading it fails.
public static NETSetupConfig SetMemory(this NETSetupConfig config, string machineName, string size)
| Name | Type | Description |
|---|---|---|
config |
NETSetupConfig |
|
machineName |
string |
Machine in the config to size, e.g. "devBuilder03". |
size |
string |
Memory size parsed by |
Returns: NETSetupConfig
4.24. SetWindowsCapabilities(string, WindowsCapabilities)
Declares the Windows post-install capabilities for machineName. Unlike SetMemory(NETSetupConfig, string, string) / SetDisk(NETSetupConfig, string, string) this IS persisted to JSON - every field on WindowsCapabilities is a primitive or a plain string collection, so it round-trips. Call it from the customer .cs before serialization.
public static NETSetupConfig SetWindowsCapabilities(this NETSetupConfig config, string machineName, WindowsCapabilities capabilities)
| Name | Type | Description |
|---|---|---|
config |
NETSetupConfig |
|
machineName |
string |
Machine the capabilities apply to, e.g. "devBuilder05". |
capabilities |
WindowsCapabilities |
The capability block to attach. |
Returns: NETSetupConfig
4.25. ToProxmoxAutoInstall(ISystemHost, Server, IDisk?, bool)
public static ProxmoxAutoInstallToml ToProxmoxAutoInstall(this NETSetupConfig config, ISystemHost host, Server liberator, IDisk? targetDisk = null, bool setStaticNetworkConfiguration = false)
| Name | Type | Description |
|---|---|---|
config |
NETSetupConfig |
|
host |
ISystemHost |
|
liberator |
Server |
|
targetDisk |
IDisk? |
|
setStaticNetworkConfiguration |
bool |
Returns: ProxmoxAutoInstallToml