Skip to content

Migrating Azure Terraform to Frostmoln ​

To run a conversion, see the migration guide.

This matrix lists how the converter handles each supported resource type and attribute.

Provider configuration and tags ​

Provider block ​

Each provider "azurerm" block becomes a provider "frostmoln" block, and an aliased block keeps a literal alias. Features, subscription and credentials are not carried. azurerm has no provider-level default tags, so the frostmoln provider block gets no default_tags; each resource's own tags carry with the resource. The converted configuration declares frostmoln/frostmoln with version ~> 0.82 and requires frostmoln provider 0.82.0 or newer, so a private registry mirror must carry that version.

Tenant default tags ​

Frostmoln also has tenant default tags, a per-tenant setting of the platform. No source provider setting is tenant-scoped, and the converter never sets tenant default tags. Azure Policy assignments that add or inherit tags (for example the built-in "Inherit a tag from the resource group" policy, assigned with azurerm_resource_group_policy_assignment, azurerm_subscription_policy_assignment or azurerm_management_group_policy_assignment) are organisational controls with no converter mapping.

Tag colours ​

Azure tags have no colour. Frostmoln tag colours (set per organization, for display) have no source equivalent: set them after migration with frostmoln_tag_color or in the portal.

azurerm_application_gateway → frostmoln_application_gateway — partially supported ​

One Application Gateway becomes a family of resources: the gateway itself plus a frostmoln_appgw_listener per listener, a frostmoln_appgw_backend_pool per backend setting, a frostmoln_appgw_backend per pool address, a frostmoln_appgw_route per routing rule, a frostmoln_appgw_health_check per probe, a frostmoln_appgw_certificate per certificate, the frostmoln_appgw_config_apply without which none of it is served, and — where the source carries a waf_configuration — a frostmoln_appgw_waf_policy with its attachment and its publication, emitted in detect mode whatever the source said.

NOTHING THE GATEWAY IS TOLD IS SERVED UNTIL IT IS APPLIED. Listeners, routes, backend pools, backends, certificates and published firewall versions are authored against the gateway; the appliance keeps serving what it last acknowledged until a frostmoln_appgw_config_apply dispatches the change. A configuration that creates every listener and route and no frostmoln_appgw_config_apply plans clean, applies clean, and serves nothing. The converter therefore generates one, from the sku block — the one block Azure requires exactly once, used here as the anchor for the one-per-gateway resource rather than as a translation of the sku itself. Its triggers map is an FM-TODO: fill it with each generated listener, route, pool, backend and certificate's updated_at, so Terraform runs the apply last and re-runs it whenever one of them moves. A child left out of that map is a change the gateway is never told about.

THE SAME IS TRUE OF THE FIREWALL, TWICE OVER. A frostmoln_appgw_waf_policy is authored, not enforced: what the gateway inspects with is the last PUBLISHED version, so a policy and its rules without a frostmoln_appgw_waf_policy_publication inspect nothing at all, and a policy with no frostmoln_appgw_waf_policy_attachment is not applied anywhere. A waf_configuration block is therefore converted into all three — policy, attachment and publication — never the policy alone.

THE CONVERTED POLICY IS EMITTED IN detect MODE, WHATEVER THE SOURCE SAID, INCLUDING Prevention. Detect runs the ruleset and records what it WOULD have refused, refusing nothing. That is deliberate and it is not the safe-looking default it resembles: the converted policy inspects with the PLATFORM's managed ruleset, and your disabled_rule_group and exclusion tuning does not convert with it, so a converted gateway put straight into blocking mode would refuse legitimate traffic your Azure gateway allowed. The cost is that a Prevention-mode source applies cleanly and is NOT protected until you move it. The generated mode carries an FM-TODO saying so.

Do not lean on the publication's dry-run to catch that for you. It refuses to publish while more than max_newly_blocked requests IN ITS SAMPLE would newly be refused, and that limit defaults to 0 — but a gateway that has just been created has served nothing, so the sample is empty and the check passes vacuously. It becomes a real gate only once the gateway has seen real traffic, which is also when it is worth moving to block.

The pieces do not carry each other's identifiers. Azure joins a listener, a backend address pool and a backend setting by NAME inside one resource; Frostmoln joins them by ID across separate resources, and the converter does not resolve those name references. Every generated route therefore needs its listener_id and backend_pool_id, every generated backend its pool_id, and every generated health check its pool_id. Each is reported.

Ports move. A listener's port lives in a separate frontend_port block on Azure and on the listener itself on Frostmoln; a backend's port lives in the backend setting on Azure and on each individual backend on Frostmoln. The converter does not follow either reference, so the generated listeners and backends have no port and terraform validate refuses them until you set one. That is deliberate — a guessed port is a gateway answering on the wrong socket, or forwarding to the wrong service, with nothing to show for it.

The frontend is not optional. A frostmoln_application_gateway attaches a public address in both public_ip_mode values, so an Azure gateway with a private-only frontend_ip_configuration has no equivalent: the converted gateway is reachable from the internet. Restrict it with allowed_cidrs, denied_cidrs and geo_block_mode on each generated listener.

Backends are not reachable just because they exist. A frostmoln_appgw_backend must lie inside the gateway's own VPC range, and creating one does not open the path to it — the platform never edits your security groups. Add one frostmoln_appgw_backend_authorization per (security group, port), not one per backend.

No counterpart, checked against the pinned provider rather than assumed: frostmoln_application_gateway has no autoscaling (capacity is the flavor — see the frostmoln_appgw_flavors data source), no HTTP/2, FIPS, zone, buffering or custom-error-page setting, and no managed identity. A rewrite_rule_set has no equivalent as a set — header edits become request_headers_set, request_headers_remove and response_headers_set on a frostmoln_appgw_route and a path rewrite becomes its rewrite_path_prefix, but a condition-based rewrite does not convert. A redirect_configuration has no equivalent either: a frostmoln_appgw_route only forwards, and the one redirect the platform offers is redirect_to_https on a frostmoln_appgw_listener. ssl_policy has no cipher-suite list — a frostmoln_appgw_listener takes tls_min_version and a tls_cipher_profile of modern or intermediate.

AttributeOutcomeNotes
nameMapped to name
locationNot needed on Frostmolnthe platform is single-region; the default region applies.
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsConvertedTags become the gateway's frostmoln tags (at most 32, your tenant's default tags included). The generated listeners, pools, backends, routes, certificates and firewall resources do not inherit them — each has its own tags.
zonesNot carried overfrostmoln_application_gateway has no availability-zone setting.
http2_enabledNot carried overfrostmoln_application_gateway has no HTTP/2 setting.
fips_enabledNot carried overfrostmoln_application_gateway has no FIPS setting.
globalNot carried overfrostmoln_application_gateway has no request- or response-buffering setting.
custom_error_configurationNot carried overfrostmoln_application_gateway has no custom-error-page setting.
identityManual action requiredfrostmoln_application_gateway has no managed-identity setting; a certificate is uploaded to a frostmoln_appgw_certificate rather than read from an external vault.
private_link_configurationManual action requiredthere is no Private Link equivalent; the gateway is reached over its public address, restricted with allowed_cidrs, denied_cidrs and geo_block_mode on each frostmoln_appgw_listener.
gateway_ip_configurationManual action requiredthis block's subnet_id is the gateway's own subnet: set subnet_id, and the vpc_id that subnet belongs to, on the converted frostmoln_application_gateway — both are FM-TODO placeholders in the output.
frontend_ip_configurationManual action requireda frostmoln_application_gateway attaches a public address in both public_ip_mode values, so a private-only frontend has no equivalent — the converted gateway is reachable from the internet. Set public_ip_mode to allocated to draw an address from the pool, or to selected with public_ip_id for one you already hold, and restrict who may reach it with allowed_cidrs, denied_cidrs and geo_block_mode on each frostmoln_appgw_listener.
frontend_portManual action requireda frostmoln_appgw_listener carries its own port, so there is nothing for this block to convert to; copy each port onto the listener generated from the http_listener (or listener) block that names it.
skuManual action requiredsize is the gateway's flavor_id, which is an FM-TODO placeholder in the output; pick one from the frostmoln_appgw_flavors data source. The converter also uses this block — the one Azure requires exactly once — as the anchor for the gateway's frostmoln_appgw_config_apply.
autoscale_configurationManual action requiredfrostmoln_application_gateway has no autoscaling: capacity is the flavor. Pick a size from the frostmoln_appgw_flavors data source that covers the peak this block was scaling to.
ssl_policyManual action requireda frostmoln_appgw_listener takes tls_min_version (1.2 or 1.3) and tls_cipher_profile (modern or intermediate); an explicit cipher-suite list and a named predefined policy have no equivalent. Set the two attributes on each generated listener.
ssl_profileManual action requiredclient-certificate (mutual TLS) profiles have no equivalent; a frostmoln_appgw_listener presents a certificate and does not request one.
trusted_client_certificateManual action requiredclient-certificate (mutual TLS) trust has no equivalent; a frostmoln_appgw_listener does not request a client certificate.
trusted_root_certificateManual action requireda backend CA is supplied inline: set tls_ca_certificate on the frostmoln_appgw_backend_pool that re-encrypts to the backend, together with tls_server_name and tls_verify_backend.
url_path_mapManual action requiredpath routing is per route on Frostmoln: each path_rule becomes its own frostmoln_appgw_route with path_match_type (prefix, exact or regex) and path, and the default pool becomes a catch-all route with a higher priority number. The converter does not generate them.
rewrite_rule_setManual action requiredthere is no rewrite-rule-set equivalent: header edits become request_headers_set, request_headers_remove and response_headers_set on a frostmoln_appgw_route, and a path rewrite becomes its rewrite_path_prefix. A rewrite that depends on a condition does not convert.
redirect_configurationManual action requireda frostmoln_appgw_route only forwards; the one redirect the platform offers is redirect_to_https on a frostmoln_appgw_listener. Any other redirect has to be served by the application behind the gateway.
firewall_policy_idManual action requiredconvert the azurerm_web_application_firewall_policy this points at, then attach it with a frostmoln_appgw_waf_policy_attachment and publish it with a frostmoln_appgw_waf_policy_publication. A frostmoln_appgw_waf_policy on its own is authored, not enforced: the gateway inspects with the last published version, so an unattached or unpublished policy inspects nothing while the plan looks complete.
force_firewall_policy_associationNot needed on Frostmolnthere is no association-mode flag; a policy applies where a frostmoln_appgw_waf_policy_attachment puts it.

Inline sku blocks are split into frostmoln_appgw_config_apply resources:

Block attributeOutcomeNotes
nameManual action requiredsize is the gateway's flavor_id: pick one from the frostmoln_appgw_flavors data source and set it on the converted frostmoln_application_gateway, where it is an FM-TODO placeholder.
tierManual action requiredthere is no tier concept; a gateway always carries the Web Application Firewall, which is attached and published separately (frostmoln_appgw_waf_policy, frostmoln_appgw_waf_policy_attachment, frostmoln_appgw_waf_policy_publication).
capacityManual action requiredcapacity is the flavor: there is no instance count. Pick a size from the frostmoln_appgw_flavors data source that covers the peak this capacity was serving.

Inline http_listener blocks are split into frostmoln_appgw_listener resources:

Block attributeOutcomeNotes
nameMapped to name
protocolConverted
frontend_port_nameManual action requireda frostmoln_appgw_listener carries its own port and the converter does not follow this name reference, so the generated listener has NO port and terraform validate refuses it until you set one. Copy the number from the frontend_port block this name points at — a guessed port is a listener bound to the wrong socket.
frontend_ip_configuration_nameManual action requiredthere is no per-listener frontend: a frostmoln_application_gateway has one address, and it is public. Restrict who may reach this listener with allowed_cidrs, denied_cidrs and geo_block_mode.
host_nameManual action requiredhost matching moves to the route: set host on each frostmoln_appgw_route that hangs off this listener. A listener with no host match serves every host.
host_namesManual action requiredhost matching moves to the route, one host per frostmoln_appgw_route (host takes a single name or a wildcard such as *.example.com). A listener with no host match serves every host.
ssl_certificate_nameManual action requireda certificate is selected by id: set default_certificate_id — or add it to sni_certificate_ids — to the frostmoln_appgw_certificate generated from the ssl_certificate block this name points at.
ssl_profile_nameManual action requiredclient-certificate (mutual TLS) profiles have no equivalent; a frostmoln_appgw_listener presents a certificate and does not request one.
require_sniNot carried overthere is no require_sni flag: a frostmoln_appgw_listener serves default_certificate_id when no SNI matches and picks from sni_certificate_ids when one does.
firewall_policy_idManual action requireda per-listener firewall is an overlay-scoped frostmoln_appgw_waf_policy attached with a frostmoln_appgw_waf_policy_attachment naming this listener. An overlay is compiled WITHOUT the managed ruleset and carries only your own rules, and like every policy it inspects nothing until a frostmoln_appgw_waf_policy_publication publishes it.
custom_error_configurationNot carried overfrostmoln_appgw_listener has no custom-error-page setting.

Inline listener blocks are split into frostmoln_appgw_listener resources:

Block attributeOutcomeNotes
nameMapped to name
protocolConverted
frontend_port_nameManual action requireda frostmoln_appgw_listener carries its own port and the converter does not follow this name reference, so the generated listener has NO port and terraform validate refuses it until you set one. Copy the number from the frontend_port block this name points at — a guessed port is a listener bound to the wrong socket.
frontend_ip_configuration_nameManual action requiredthere is no per-listener frontend: a frostmoln_application_gateway has one address, and it is public. Restrict who may reach this listener with allowed_cidrs, denied_cidrs and geo_block_mode.
host_namesManual action requiredhost matching moves to the route, one host per frostmoln_appgw_route (host takes a single name or a wildcard such as *.example.com). A listener with no host match serves every host.
ssl_certificate_nameManual action requireda certificate is selected by id: set default_certificate_id — or add it to sni_certificate_ids — to the frostmoln_appgw_certificate generated from the ssl_certificate block this name points at.
ssl_profile_nameManual action requiredclient-certificate (mutual TLS) profiles have no equivalent; a frostmoln_appgw_listener presents a certificate and does not request one.

Inline backend_http_settings blocks are split into frostmoln_appgw_backend_pool resources:

Block attributeOutcomeNotes
nameMapped to name
protocolConverted
cookie_based_affinityConverted to session_affinityEnabled becomes session_affinity = "cookie", Disabled becomes "none". A cookie pool also REQUIRES session_cookie_name, and Azure does not: affinity_cookie_name is optional there and defaults server-side, so a source that relied on that default converts to a pool the provider refuses until you name the cookie yourself.
affinity_cookie_nameMapped to session_cookie_nameCarried over as session_cookie_name. It is optional on Azure and required on a cookie-affinity Frostmoln pool, so if the source left it out, pick a name here.
portManual action requiredthe port belongs to each backend on Frostmoln, not to the pool: set it on every frostmoln_appgw_backend in this pool. Those backends are generated from the backend_address_pool a request_routing_rule pairs with this setting, and terraform validate refuses a backend without a port.
request_timeoutManual action requiredrequest_timeout is in SECONDS and frostmoln_appgw_backend_pool.timeout_response_ms is in MILLISECONDS, so it is refused rather than dropped: set timeout_response_ms to this value multiplied by 1000, and set timeout_connect_ms alongside it. It is not converted for you because the platform default is far shorter than a long Azure request_timeout, and a pool that silently inherits that default returns 504 on exactly the slow requests this setting existed to allow.
pathManual action requireda forwarded-path prefix belongs to the route: set rewrite_path_prefix on each frostmoln_appgw_route that forwards to this pool.
host_nameManual action requiredthere is no per-pool Host override: set the header with request_headers_set on each frostmoln_appgw_route that forwards to this pool. On an https pool the name VERIFIED against the backend certificate is tls_server_name, which is a separate setting.
sni_nameMapped to tls_server_name
pick_host_name_from_backend_addressNot carried overthere is no derive-the-Host-from-the-backend setting; set the header explicitly with request_headers_set on the route.
probe_nameManual action requireda health check is keyed on the pool: set pool_id on the frostmoln_appgw_health_check generated from the probe block this name points at.
trusted_root_certificate_namesManual action requireda backend CA is supplied inline: set tls_ca_certificate on this pool to the PEM bundle, alongside tls_server_name and tls_verify_backend.
certificate_chain_validation_enabledManual action requiredbackend certificate verification is tls_verify_backend on this pool. Leave it unset to keep the platform default — an explicit false disables verification, which is why the converter never writes one for you.
sni_validation_enabledManual action requiredbackend certificate verification is tls_verify_backend on this pool, and the name checked against the certificate is tls_server_name; there is no separate SNI-validation switch.
dedicated_backend_connection_enabledNot carried overfrostmoln_appgw_backend_pool has no connection-reuse setting.
connection_drainingNot carried overfrostmoln_appgw_backend_pool has no connection-draining setting.

Inline backend blocks are split into frostmoln_appgw_backend_pool resources:

Block attributeOutcomeNotes
nameMapped to name
protocolConverted
portManual action requiredthe port belongs to each backend on Frostmoln, not to the pool: set it on every frostmoln_appgw_backend in this pool. Those backends are generated from the backend_address_pool a routing_rule pairs with this setting, and terraform validate refuses a backend without a port.
timeout_in_secondsManual action requiredtimeout_in_seconds is in SECONDS and frostmoln_appgw_backend_pool.timeout_response_ms is in MILLISECONDS, so it is refused rather than dropped: set timeout_response_ms to this value multiplied by 1000, and set timeout_connect_ms alongside it. It is not converted for you because the platform default is far shorter than a long Azure timeout, and a pool that silently inherits that default returns 504 on exactly the slow requests this setting existed to allow.
host_nameManual action requiredthere is no per-pool Host override: set the header with request_headers_set on each frostmoln_appgw_route that forwards to this pool. On an https pool the name VERIFIED against the backend certificate is tls_server_name, which is a separate setting.
probe_nameManual action requireda health check is keyed on the pool: set pool_id on the frostmoln_appgw_health_check generated from the probe block this name points at.
trusted_root_certificate_namesManual action requireda backend CA is supplied inline: set tls_ca_certificate on this pool to the PEM bundle, alongside tls_server_name and tls_verify_backend.
client_ip_preservation_enabledNot carried overfrostmoln_appgw_backend_pool has no client-IP-preservation setting; the gateway terminates the connection and forwards.

Inline backend_address_pool blocks are split into frostmoln_appgw_backend resources (one per element of ip_addresses):

Block attributeOutcomeNotes
ip_addressesOne resource per element, becomes address
nameManual action requiredthe pool identity is carried by the frostmoln_appgw_backend_pool generated from the backend setting a routing rule pairs with this address pool. Each generated frostmoln_appgw_backend still needs that pool_id and a port — terraform validate refuses a backend without one — and an address only becomes reachable once a frostmoln_appgw_backend_authorization opens the path, one per (security group, port).
fqdnsManual action requireda frostmoln_appgw_backend is named by an IP address inside the gateway's own VPC range; an FQDN backend, and any address outside that range, is refused. Resolve each name to its VPC address, or put the external service behind something inside the VPC.

Inline request_routing_rule blocks are split into frostmoln_appgw_route resources:

Block attributeOutcomeNotes
nameMapped to name
priorityMapped to priorityCarried over unchanged. Azure allows 1–20000 and the Frostmoln range is narrower, so a rule sitting at the top of Azure's range — a catch-all at 20000 is the common one — is refused at apply. Renumber those into a compact sequence; only the relative order matters.
http_listener_nameManual action requireda route hangs off a listener by id: set listener_id to the frostmoln_appgw_listener generated from the listener block this name points at. terraform validate refuses a route without one.
backend_address_pool_nameManual action requireda route forwards to a pool by id: set backend_pool_id to the frostmoln_appgw_backend_pool generated from the backend setting paired with this address pool. terraform validate refuses a route without one.
backend_http_settings_nameManual action requiredthe frostmoln_appgw_backend_pool generated from this backend setting is the one to point backend_pool_id at — on Frostmoln the pool carries the protocol, affinity and timeouts that this block carried on Azure.
rule_typeManual action requiredthere is no rule-type switch: a path-based rule becomes one frostmoln_appgw_route per path, each with path_match_type (prefix, exact or regex) and path. Priority is explicit and LOWER WINS — there is no longest-prefix rule — so give the specific routes low numbers and the catch-all a high one.
url_path_map_nameManual action requiredeach path_rule in the named url_path_map becomes its own frostmoln_appgw_route with path_match_type and path, and the map's default pool becomes a catch-all route with a higher priority number. The converter does not generate them.
redirect_configuration_nameManual action requireda frostmoln_appgw_route only forwards; the one redirect the platform offers is redirect_to_https on a frostmoln_appgw_listener. Any other redirect has to be served by the application behind the gateway.
rewrite_rule_set_nameManual action requiredheader edits become request_headers_set, request_headers_remove and response_headers_set on this route, and a path rewrite becomes its rewrite_path_prefix. A rewrite that depends on a condition does not convert.

Inline routing_rule blocks are split into frostmoln_appgw_route resources:

Block attributeOutcomeNotes
nameMapped to name
priorityMapped to priorityCarried over unchanged. Azure allows 1–20000 and the Frostmoln range is narrower, so a rule sitting at the top of Azure's range — a catch-all at 20000 is the common one — is refused at apply. Renumber those into a compact sequence; only the relative order matters.
listener_nameManual action requireda route hangs off a listener by id: set listener_id to the frostmoln_appgw_listener generated from the listener block this name points at. terraform validate refuses a route without one.
backend_address_pool_nameManual action requireda route forwards to a pool by id: set backend_pool_id to the frostmoln_appgw_backend_pool generated from the backend setting paired with this address pool. terraform validate refuses a route without one.
backend_nameManual action requiredthe frostmoln_appgw_backend_pool generated from this backend setting is the one to point backend_pool_id at — on Frostmoln the pool carries the protocol and timeouts that this block carried on Azure.

Inline probe blocks are split into frostmoln_appgw_health_check resources:

Block attributeOutcomeNotes
protocolConverted
pathMapped to path
intervalMapped to interval_seconds
timeoutMapped to timeout_seconds
unhealthy_thresholdMapped to unhealthy_threshold
nameNot carried overfrostmoln_appgw_health_check has no name; a pool has at most one check and the resource is keyed on the pool.
hostNot carried overfrostmoln_appgw_health_check has no probe-host setting; it requests path on each backend's own address and port.
pick_host_name_from_backend_http_settingsNot carried overfrostmoln_appgw_health_check has no probe-host setting.
portNot carried overhealth checks run against each backend's own port.
matchManual action requiredexpected_status takes one status or one range (200, or 200-299), not a list, and there is no response-body match. Set expected_status to the range that covers what this block accepted.
minimum_serversNot carried overfrostmoln_appgw_health_check has no minimum-healthy-server setting.
proxy_protocol_header_enabledNot carried overfrostmoln_appgw_health_check has no PROXY-protocol setting.

Inline ssl_certificate blocks are split into frostmoln_appgw_certificate resources:

Block attributeOutcomeNotes
nameMapped to name
dataManual action requireda frostmoln_appgw_certificate takes a PEM chain in chain_pem (leaf first) and its key separately, not a base64 PFX, and the converter never moves key material for you. Convert the bundle to PEM and set chain_pem, then supply the key as private_key_pem_wo with private_key_pem_wo_version — private_key_pem is written to Terraform state in plaintext. terraform validate refuses a certificate without chain_pem.
passwordManual action requiredthere is no bundle passphrase: the key is supplied as PEM, and private_key_pem_wo keeps it out of the plan and out of state.
key_vault_secret_idManual action requireda certificate held in an external vault has no equivalent; upload the PEM chain and key to a frostmoln_appgw_certificate, or attach a platform-issued certificate by id.

Inline waf_configuration blocks are split into frostmoln_appgw_waf_policy resources:

Block attributeOutcomeNotes
firewall_modeManual action requiredfirewall_mode does NOT carry over, and a Prevention-mode source is the case this refusal exists for. The converted policy inspects with the PLATFORM's managed ruleset, and your disabled_rule_group and exclusion tuning is not converted with it — so emitting block here would put a ruleset you have never run into refusing mode against your production traffic, with none of the suppressions you built up in Azure. The converted policy is emitted in detect instead, whatever this said: it runs the ruleset and refuses nothing. Read the generated frostmoln_appgw_waf_policy's mode FM-TODO, then move to block deliberately once the publication's dry-run has shown you what would newly be refused.
enabledManual action requiredthere is no enable switch on a Web Application Firewall policy: a policy inspects where a frostmoln_appgw_waf_policy_attachment puts it, and only once a frostmoln_appgw_waf_policy_publication has published it. Where this block was disabled, remove the generated attachment and publication rather than leaving an enforced policy behind; where it was enabled, check that the generated mode is the one you want before the publication runs.
max_request_body_size_kbManual action requiredmax_request_body_size_kb is in KILOBYTES and frostmoln_appgw_waf_policy.request_body_limit_bytes is in bytes, between 4096 and 1048576 with a default of 40960. Set it deliberately: a body over the limit is refused 413 rather than passed uninspected, so this is also the largest request your application receives.
file_upload_limit_mbNot carried overthere is no separate upload limit; request_body_limit_bytes bounds every request body.
request_body_checkNot carried overthere is no request-body-inspection switch; request_body_limit_bytes bounds what is inspected and anything larger is refused.
rule_set_typeNot carried overthere is one managed ruleset and it is the platform's; managed_ruleset_version is read, not written.
rule_set_versionNot carried overthe managed ruleset version is the platform's to migrate; managed_ruleset_version is read, not written.
disabled_rule_groupManual action requiredturning a managed rule off is a frostmoln_appgw_waf_rule with kind = "managedOverride", managed_secrule_id and managed_action = "disable" — one per rule, not per group. Prefer a frostmoln_appgw_waf_exclusion where the rule is only a false positive on one field: it narrows the rule instead of switching it off everywhere. Either way the change lands on the draft and inspects nothing until it is published.
exclusionManual action requiredeach exclusion becomes a frostmoln_appgw_waf_exclusion with variable, selector and an optional path_pattern, targeting one managed rule (target_secrule_id) or one tag (target_tag). It lands on the draft and takes effect only once a frostmoln_appgw_waf_policy_publication publishes it.

Each split also creates a frostmoln_appgw_waf_policy_attachment resource.

Each split also creates a frostmoln_appgw_waf_policy_publication resource.

  • The converter adds flavor_id automatically. frostmoln_application_gateway requires a flavor_id; pick a size from the frostmoln_appgw_flavors data source, which also carries each size's structural limits.
  • The converter adds vpc_id automatically. frostmoln_application_gateway requires vpc_id; set it to the converted VPC the gateway's subnet belongs to.
  • The converter adds subnet_id automatically. frostmoln_application_gateway requires subnet_id; set it to the converted subnet named by the gateway_ip_configuration block.

azurerm_dns_a_record → frostmoln_dns_record — partially supported ​

A records map to frostmoln_dns_record; the record name becomes zone-relative and the IP list becomes records.

The zone is referenced by name on Azure — a direct azurerm_dns_zone reference is rewritten to the converted zone id, otherwise set zone_id manually. Alias records (target_resource_id) have no equivalent.

AttributeOutcomeNotes
nameConvertedAzure record names are already relative to the zone, which is exactly what Frostmoln takes: they pass through unchanged (lowercased). A name that repeats the zone suffix is flagged for review.
zone_nameConverted to zone_idA direct azurerm_dns_zone reference is rewritten to the converted frostmoln_dns_zone id.
recordsMapped to records
ttlMapped to ttl
target_resource_idManual action requiredalias records have no equivalent; use an explicit A record with IP addresses.
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsNot carried overfrostmoln_dns_record has no tags.
  • The converter adds type automatically. the record type is fixed by the source resource type.

azurerm_dns_aaaa_record → frostmoln_dns_record — partially supported ​

AAAA records map to frostmoln_dns_record; the record name becomes zone-relative and the IPv6 list becomes records.

The zone is referenced by name on Azure — a direct azurerm_dns_zone reference is rewritten to the converted zone id, otherwise set zone_id manually. Alias records (target_resource_id) have no equivalent.

AttributeOutcomeNotes
nameConvertedAzure record names are already relative to the zone, which is exactly what Frostmoln takes: they pass through unchanged (lowercased). A name that repeats the zone suffix is flagged for review.
zone_nameConverted to zone_idA direct azurerm_dns_zone reference is rewritten to the converted frostmoln_dns_zone id.
recordsMapped to records
ttlMapped to ttl
target_resource_idManual action requiredalias records have no equivalent; use an explicit AAAA record with IP addresses.
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsNot carried overfrostmoln_dns_record has no tags.
  • The converter adds type automatically. the record type is fixed by the source resource type.

azurerm_dns_caa_record → frostmoln_dns_record — partially supported ​

CAA records BLOCK the conversion: the structured record blocks cannot be aggregated into the required records set, and a plausible placeholder would be dangerous.

frostmoln_dns_record takes records as rdata strings ("<flags> <tag> <value>"). The Azure record blocks have no direct equivalent, and inventing a value could block legitimate certificate issuance — so the whole record is left unconverted for you to recreate by hand.

AttributeOutcomeNotes
nameConvertedAzure record names are already relative to the zone, which is exactly what Frostmoln takes: they pass through unchanged (lowercased). A name that repeats the zone suffix is flagged for review.
zone_nameConverted to zone_idA direct azurerm_dns_zone reference is rewritten to the converted frostmoln_dns_zone id.
recordManual action requiredCAA record blocks cannot be aggregated into the records set; recreate them as rdata strings ("<flags> <tag> <value>") — a placeholder value could block certificate issuance.
ttlMapped to ttl
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsNot carried overfrostmoln_dns_record has no tags.
  • The converter adds type automatically. the record type is fixed by the source resource type.

azurerm_dns_cname_record → frostmoln_dns_record — partially supported ​

CNAME records map to frostmoln_dns_record; the single record target becomes a one-element records set.

The zone is referenced by name on Azure — a direct azurerm_dns_zone reference is rewritten to the converted zone id, otherwise set zone_id manually. Alias records (target_resource_id) have no equivalent.

AttributeOutcomeNotes
nameConvertedAzure record names are already relative to the zone, which is exactly what Frostmoln takes: they pass through unchanged (lowercased). A name that repeats the zone suffix is flagged for review.
zone_nameConverted to zone_idA direct azurerm_dns_zone reference is rewritten to the converted frostmoln_dns_zone id.
recordConverted to recordsThe single CNAME target becomes a one-element records set.
ttlMapped to ttl
target_resource_idManual action requiredalias records have no equivalent; use an explicit CNAME record with a target value.
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsNot carried overfrostmoln_dns_record has no tags.
  • The converter adds type automatically. the record type is fixed by the source resource type.

azurerm_dns_mx_record → frostmoln_dns_record — partially supported ​

MX records map to frostmoln_dns_record; literal record blocks are aggregated into rdata strings, and anything computed blocks the conversion.

frostmoln_dns_record takes records as rdata strings ("<preference> <exchange>", e.g. "10 mail.example.com."). Record blocks whose preference and exchange are literals are aggregated for you and flagged for review — verify them, because wrong values misroute mail. A record block with a computed value leaves the whole record unconverted. Azure allows an omitted name for the apex; set "@" if needed.

AttributeOutcomeNotes
nameConvertedAzure record names are already relative to the zone, which is exactly what Frostmoln takes: they pass through unchanged (lowercased). A name that repeats the zone suffix is flagged for review.
zone_nameConverted to zone_idA direct azurerm_dns_zone reference is rewritten to the converted frostmoln_dns_zone id.
recordConverted to recordsLiteral record blocks are aggregated into "<preference> <exchange>" rdata strings and flagged for review; a computed value blocks the whole record.
ttlMapped to ttl
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsNot carried overfrostmoln_dns_record has no tags.
  • The converter adds type automatically. the record type is fixed by the source resource type.

azurerm_dns_ns_record → frostmoln_dns_record — partially supported ​

NS records map to frostmoln_dns_record; the record name becomes zone-relative and the name-server list becomes records.

The zone is referenced by name on Azure — a direct azurerm_dns_zone reference is rewritten to the converted zone id, otherwise set zone_id manually.

AttributeOutcomeNotes
nameConvertedAzure record names are already relative to the zone, which is exactly what Frostmoln takes: they pass through unchanged (lowercased). A name that repeats the zone suffix is flagged for review.
zone_nameConverted to zone_idA direct azurerm_dns_zone reference is rewritten to the converted frostmoln_dns_zone id.
recordsMapped to records
ttlMapped to ttl
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsNot carried overfrostmoln_dns_record has no tags.
  • The converter adds type automatically. the record type is fixed by the source resource type.

azurerm_dns_ptr_record → frostmoln_dns_record — partially supported ​

PTR records map to frostmoln_dns_record; the record name becomes zone-relative and the target list becomes records.

The zone is referenced by name on Azure — a direct azurerm_dns_zone reference is rewritten to the converted zone id, otherwise set zone_id manually.

AttributeOutcomeNotes
nameConvertedAzure record names are already relative to the zone, which is exactly what Frostmoln takes: they pass through unchanged (lowercased). A name that repeats the zone suffix is flagged for review.
zone_nameConverted to zone_idA direct azurerm_dns_zone reference is rewritten to the converted frostmoln_dns_zone id.
recordsMapped to records
ttlMapped to ttl
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsNot carried overfrostmoln_dns_record has no tags.
  • The converter adds type automatically. the record type is fixed by the source resource type.

azurerm_dns_srv_record → frostmoln_dns_record — partially supported ​

SRV records BLOCK the conversion: the structured record blocks cannot be aggregated into the required records set, and a plausible placeholder would be dangerous.

frostmoln_dns_record takes records as rdata strings ("<priority> <weight> <port> <target>"). The Azure record blocks have no direct equivalent, and inventing a value could misdirect service discovery — so the whole record is left unconverted for you to recreate by hand.

AttributeOutcomeNotes
nameConvertedAzure record names are already relative to the zone, which is exactly what Frostmoln takes: they pass through unchanged (lowercased). A name that repeats the zone suffix is flagged for review.
zone_nameConverted to zone_idA direct azurerm_dns_zone reference is rewritten to the converted frostmoln_dns_zone id.
recordManual action requiredSRV record blocks cannot be aggregated into the records set; recreate them as rdata strings ("<priority> <weight> <port> <target>") — a placeholder value could misdirect service discovery.
ttlMapped to ttl
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsNot carried overfrostmoln_dns_record has no tags.
  • The converter adds type automatically. the record type is fixed by the source resource type.

azurerm_dns_txt_record → frostmoln_dns_record — partially supported ​

TXT records map to frostmoln_dns_record; literal record blocks are aggregated into the records set, and anything computed blocks the conversion.

frostmoln_dns_record takes records as a set of TXT values. Record blocks whose values are literals are aggregated for you and flagged for review — verify them, because SPF/DKIM/verification records break silently if a value is wrong. A record block with a computed value leaves the whole record unconverted: inventing a value there would be dangerous.

AttributeOutcomeNotes
nameConvertedAzure record names are already relative to the zone, which is exactly what Frostmoln takes: they pass through unchanged (lowercased). A name that repeats the zone suffix is flagged for review.
zone_nameConverted to zone_idA direct azurerm_dns_zone reference is rewritten to the converted frostmoln_dns_zone id.
recordConverted to recordsLiteral record blocks are aggregated into the records set and flagged for review; a computed value blocks the whole record.
ttlMapped to ttl
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsNot carried overfrostmoln_dns_record has no tags.
  • The converter adds type automatically. the record type is fixed by the source resource type.

azurerm_dns_zone → frostmoln_dns_zone — partially supported ​

Public DNS zones map to frostmoln_dns_zone; the zone name is normalized to a lowercase FQDN and a required SOA email is synthesized as a placeholder.

Zone names are normalized to lowercase fully qualified domains with a trailing dot. The required SOA admin email cannot come from Azure — an FM-TODO placeholder is emitted. SOA record tuning is platform-managed.

AttributeOutcomeNotes
nameConvertedZone names are normalized to a lowercase FQDN with a trailing dot, e.g. "example.com."
soa_recordNot carried overSOA record parameters are platform-managed; set the zone email instead.
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsConverted
  • The converter adds email automatically. the SOA admin email is required; replace the placeholder with a real contact address.

azurerm_key_vault_secret → frostmoln_secret — partially supported ​

Key Vault secrets map to frostmoln_secret; the secret value and content type carry over.

The secret is no longer scoped to a vault — access is granted from the principal side, with a frostmoln_iam_policy attached by frostmoln_iam_policy_attachment to the api_key, workload_identity, or group that needs it. Expiry and activation dates have no equivalent; version history is platform-managed.

AttributeOutcomeNotes
nameMapped to name
valueMapped to secret_value
content_typeMapped to content_type
key_vault_idNot needed on Frostmolnsecrets are not scoped to a vault; access is granted with a frostmoln_iam_policy attached to the principal that needs the secret.
expiration_dateNot carried oversecret expiry has no equivalent; rotate secrets explicitly.
not_before_dateNot carried oversecret activation dates have no equivalent.
tagsConverted

azurerm_kubernetes_cluster → frostmoln_kubernetes_cluster — partially supported ​

AKS clusters map to frostmoln_kubernetes_cluster; the default_node_pool becomes the required initial_node_pool (its vm_size reported as a size, with flavor_id left for you to set), and managed identity / RBAC / network profiles are dropped.

The API endpoint's exposure does NOT carry over — it is not a per-cluster setting on the platform (see private_cluster_enabled). Public WORKLOAD traffic has no cluster-level equivalent either: a managed cluster is NOT given an ingress load balancer, so there is no attribute for an AKS public entrypoint to convert into and none is emitted. Expose a workload the standard Kubernetes way instead — a Service of type=LoadBalancer inside the cluster, which the platform provisions a load balancer for. That is an in-cluster step (kubectl/Helm), not Terraform, so this conversion cannot do it for you. And it is only half a replacement today: such a Service can be given an INTERNAL address only, because a per-Service PUBLIC address is not built yet, so a publicly reachable workload entrypoint is an open GAP. Do not assume the converted configuration exposes anything — carry the source's public entrypoint forward on your migration checklist and plan it by hand. frostmoln_kubernetes_cluster requires a VPC and subnet — vpc_id and subnet_id are synthesized as placeholders (set them to the converted network; AKS takes its subnet from default_node_pool.vnet_subnet_id). Managed identities, service principals, Azure AD RBAC, add-on profiles, and network plugins have no equivalent and are dropped. Workload identity IS supported on managed Kubernetes — it is the optional workload-identity cluster add-on (add it to addons, at create or later) rather than a cluster flag, so workload_identity_enabled carries nothing over. Each azurerm_federated_identity_credential has to be rewritten by hand as a frostmoln_workload_identity_binding, because the source block carries neither the cluster nor the permissions the binding needs; grant the binding with a frostmoln_iam_policy attached by frostmoln_iam_policy_attachment (its flat scopes list is optional and coarser). Verify the Kubernetes version is offered on the platform.

AttributeOutcomeNotes
nameMapped to name
kubernetes_versionMapped to versionVerify the version is offered on the platform (fm kubernetes version list).
default_node_poolConverted to initial_node_poolnode_count and name carry over; autoscaling/zones/taints are dropped. vm_size is parsed for its vCPUs and RAM and reported; flavor_id is left as an FM-TODO to set from fm kubernetes flavor list. If vm_size comes from a variable, the pool still converts and the size is simply not reported.
dns_prefixNot carried overthe API endpoint hostname is platform-managed.
dns_prefix_private_clusterNot carried overthe API endpoint hostname is platform-managed.
identityManual action requiredmanaged identities have no equivalent; the platform manages cluster identity.
service_principalManual action requiredservice principals have no equivalent; the platform manages cluster identity.
role_based_access_control_enabledNot needed on FrostmolnRBAC is always enabled and platform-managed.
azure_active_directory_role_based_access_controlManual action requiredAzure AD integration has no equivalent; access is governed by platform roles.
network_profileNot carried overthe CNI, pod CIDR, and service CIDR are platform-managed.
private_cluster_enabledNot carried overThe Kubernetes API endpoint's exposure is not configurable on the platform, so there is nothing to set here. A private AKS cluster carries over faithfully — the managed control plane's API endpoint is private by default and is reached from inside your VPC. A PUBLIC AKS cluster does NOT: plan for kubectl access from inside the VPC, or through the platform's authenticated access path. If what you actually exposed publicly was your WORKLOAD traffic, that has no setting on the converted cluster either: a managed cluster is not given an ingress load balancer, and a workload is exposed with a Kubernetes Service of type=LoadBalancer inside the cluster. Only INTERNAL addresses are available for such a Service today, so a PUBLIC workload entrypoint is an open GAP — plan it as manual work rather than expecting the converted configuration to provide one.
oidc_issuer_enabledNot carried overthe OIDC issuer is platform-managed.
workload_identity_enabledNot carried overWorkload identity is available on managed Kubernetes as the optional workload-identity cluster add-on rather than a cluster flag, so there is nothing to set here. Add workload-identity to the frostmoln_kubernetes_cluster addons set, when the cluster is created or later (it is applied in place to a running cluster). Bind each Kubernetes service account with frostmoln_workload_identity_binding, granted by a frostmoln_iam_policy attached with frostmoln_iam_policy_attachment (its flat scopes list is optional and coarser).
azure_policy_enabledNot carried overAzure Policy add-ons have no equivalent.
oms_agentNot carried overthe monitoring agent has no equivalent; use the platform's observability.
key_vault_secrets_providerManual action requiredthe Key Vault CSI driver has no equivalent; use the secrets service.
sku_tierNot carried overthe control-plane SKU tier is platform-managed.
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
node_resource_groupNot needed on Frostmolnthere is no node resource group; node infrastructure is platform-managed.
locationNot needed on Frostmolnthe platform is single-region; the default region applies.
tagsConvertedTags become frostmoln tags (at most 32 per resource, your tenant's default tags included). They are cluster tags, never Kubernetes node labels; the initial node pool gets a one-time copy at create.
  • The converter adds initial_node_pool automatically. frostmoln_kubernetes_cluster requires an initial node pool; set flavor_id to a platform flavor (see fm kubernetes flavor list).
  • The converter adds version automatically. frostmoln_kubernetes_cluster requires a version; set it to an offered version (fm kubernetes version list).
  • The converter adds vpc_id automatically. frostmoln_kubernetes_cluster requires a VPC; set vpc_id to the converted frostmoln_vpc id.
  • The converter adds subnet_id automatically. frostmoln_kubernetes_cluster requires a subnet; set subnet_id to the converted frostmoln_subnet id (AKS takes it from default_node_pool.vnet_subnet_id).

azurerm_kubernetes_cluster_node_pool → frostmoln_kubernetes_node_pool — partially supported ​

AKS node pools map to frostmoln_kubernetes_node_pool; the vm_size's size is reported and flavor_id is left for you to set, and node_count carries over.

A node pool draws flavor_id from the Kubernetes node-flavor catalog, which is separate from the compute one — so the converter reports the source vm_size's vCPUs and RAM and leaves flavor_id as an FM-TODO for you to set from fm kubernetes flavor list. Autoscaling (min/max count), availability zones, node labels, node taints, and per-pool subnets have no equivalent and are dropped or blocked. The pool name must be a lowercase DNS label of at most 18 characters.

AttributeOutcomeNotes
nameMapped to name
kubernetes_cluster_idMapped to cluster_id
vm_sizeConverted to flavor_idParsed for its vCPUs and RAM and reported; flavor_id is left as an FM-TODO to set from fm kubernetes flavor list.
node_countMapped to node_count
auto_scaling_enabled (also matches enable_auto_scaling)Manual action requiredcluster autoscaling has no equivalent; set a fixed node_count.
min_countManual action requiredcluster autoscaling has no equivalent; set a fixed node_count.
max_countManual action requiredcluster autoscaling has no equivalent; set a fixed node_count.
vnet_subnet_idNot carried overthe subnet is set on the cluster, not per node pool.
zonesNot carried overavailability zones are platform-managed.
node_labelsNot carried overnode labels have no equivalent yet.
tagsConvertedTags become frostmoln tags on the node pool (at most 32, your tenant's default tags included) — resource tags, never Kubernetes node labels.
node_taintsManual action requirednode taints have no equivalent yet.
modeNot carried oversystem/user pool modes have no equivalent; all pools are equivalent.
os_typeNot carried overthe node OS is platform-managed.
os_disk_size_gbNot carried overthe node disk is determined by the flavor.
os_disk_typeNot carried overthe node disk is determined by the flavor.
orchestrator_versionNot carried overthe node Kubernetes version follows the cluster version.
priorityManual action requiredspot node pools have no equivalent.
eviction_policyManual action requiredspot eviction policies have no equivalent.
  • The converter adds flavor_id automatically. frostmoln_kubernetes_node_pool requires a flavor; set flavor_id to a platform flavor (see fm kubernetes flavor list).

azurerm_linux_virtual_machine → frostmoln_instance — partially supported ​

Linux VMs map to frostmoln_instance: the VM size is capability-matched to a flavor, the source image becomes a frostmoln_image lookup, the admin SSH key becomes a frostmoln_ssh_key, and the referenced network interface is absorbed (its subnet becomes subnet_id and its public IP becomes a frostmoln_public_ip).

size is matched against the platform flavor catalog and emitted as a data "frostmoln_flavor" lookup — offline a reviewed placeholder is emitted instead. The source image becomes a data "frostmoln_image" lookup whose name needs review (fm compute image list). subnet_id is contributed by the absorbed azurerm_network_interface — a VM whose interface could not be merged has no subnet and must be wired manually. Network security groups are NOT associated automatically (Azure attaches them to the interface or subnet, not the VM); set security_groups to the converted frostmoln_security_group ids. os_disk sizing is flavor/image-determined and dropped. custom_data is base64 on Azure and is decoded into user_data. Managed identities have no equivalent.

AttributeOutcomeNotes
nameMapped to name
sizeConverted to flavor_idCapability-matched against the platform flavor catalog and emitted as a frostmoln_flavor data source lookup by name.
source_image_idConverted to image_idA marketplace/shared image id becomes a frostmoln_image data source with a reviewed placeholder name.
source_image_referenceConverted to image_idpublisher/offer/sku are matched against the platform image catalog when possible, otherwise a reviewed placeholder is emitted.
admin_ssh_keyConverted to ssh_key_namesThe public key becomes a generated frostmoln_ssh_key, and ssh_key_names references it.
custom_dataConverted to user_dataAzure custom_data is base64-encoded; it is decoded into plain-text user_data.
user_dataManual action requiredset the boot script via custom_data (decoded into user_data); Azure user_data has no separate equivalent.
network_interface_idsNot needed on Frostmolnthe network interface is absorbed into the instance; subnet_id comes from the interface.
locationConvertedThe platform picks the zone; set zone to a zone id to pin one.
zoneNot carried overAzure availability-zone numbers do not map to platform zones; the platform picks the zone — set zone to pin one.
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsConverted
admin_usernameNot carried overthe admin account is platform-managed.
admin_passwordNot carried overpasswords are platform-issued; use SSH keys.
disable_password_authenticationNot needed on Frostmolnpassword authentication is platform-managed.
computer_nameNot carried overthe hostname is platform-managed.
os_diskNot carried overthe root disk is determined by the flavor and image; add a frostmoln_volume for extra storage.
identityManual action requiredmanaged identities have no equivalent; grant access with scoped API credentials.
boot_diagnosticsNot carried overboot diagnostics are platform-managed.
availability_set_idManual action requiredavailability sets have no equivalent.
proximity_placement_group_idManual action requiredproximity placement groups have no equivalent.
priorityManual action requiredspot/low-priority VMs have no equivalent; instances are always regular.
eviction_policyManual action requiredspot eviction policies have no equivalent.
patch_modeNot carried overOS patching is customer-managed inside the instance.
secretManual action requiredVM secret injection has no equivalent; use the secrets service.
additional_capabilitiesNot carried overadditional capabilities have no equivalent.
planNot carried overmarketplace plans have no equivalent.
  • The converter adds name automatically. frostmoln_instance requires a name; it is taken from the Name tag or the resource address.
  • The converter adds flavor_id automatically. frostmoln_instance requires a flavor; set flavor_id to a platform flavor (see fm compute flavor list).
  • The converter adds image_id automatically. frostmoln_instance requires an image; set image_id to a platform image (see fm compute image list).

azurerm_managed_disk → frostmoln_volume — partially supported ​

Managed disks map to frostmoln_volume; the storage account type is dropped (the platform's volume tier applies) and IOPS/throughput are set by the tier.

Volume tiers are platform-defined — pick a volume_type from the volume tiers catalog (frostmoln_volume_tiers data source) after the conversion. A disk created by copying a snapshot keeps source_resource_id as snapshot_id (verify it references a snapshot). A required volume name is synthesized from the Name tag or the resource address.

AttributeOutcomeNotes
nameMapped to name
disk_size_gbMapped to size_gb
storage_account_typeNot carried overvolume tiers are platform-defined; the default tier applies — pick a volume_type from the volume tiers catalog to change it.
source_resource_idConverted to snapshot_idverify this references a snapshot (create_option Copy); for a disk-to-disk copy, create a frostmoln_snapshot first.
create_optionNot needed on Frostmolnthe platform infers create semantics from snapshot_id.
os_typeNot carried overthe OS is determined by the instance image, not the volume.
zoneConvertedThe platform picks the zone; set zone to a zone id to pin one.
disk_iops_read_writeNot carried overIOPS are set by the volume tier.
disk_mbps_read_writeNot carried overthroughput is set by the volume tier.
disk_iops_read_onlyNot carried overIOPS are set by the volume tier.
disk_mbps_read_onlyNot carried overthroughput is set by the volume tier.
tierNot carried overperformance tiers are set by the volume tier.
source_uriManual action requiredimporting a disk from a blob URI has no equivalent.
image_reference_idManual action requiredcreating a disk from an image has no equivalent; boot instances from a frostmoln_image instead.
disk_encryption_set_idManual action requiredcustomer-managed disk encryption keys have no equivalent; volumes are encrypted with platform-managed keys.
encryption_settingsManual action requiredcustomer-managed encryption keys have no equivalent; volumes are always encrypted at rest with platform-managed keys.
network_access_policyNot carried overdisk network-access policies have no equivalent.
public_network_access_enabledNot carried overdisk public-network access has no equivalent.
max_sharesManual action requiredshared disks (multi-attach) are not supported.
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
locationNot needed on Frostmolnthe platform picks the zone.
tagsConverted
  • The converter adds name automatically. frostmoln_volume requires a name; it is taken from the Name tag or the resource address.

azurerm_mysql_flexible_server → frostmoln_mysql_instance — partially supported ​

MySQL Flexible Servers map to frostmoln_mysql_instance; the SKU's size is reported and flavor_id is left for you to set, and the storage block's size_gb becomes storage_gb. A high_availability block is DROPPED: high availability is available for PostgreSQL only.

A managed database draws flavor_id from the managed-database flavor catalog, which is separate from the compute one — so the converter reports the source SKU's vCPUs and RAM and leaves flavor_id as an FM-TODO for you to set from fm database flavor list. Administrator credentials are platform-issued and dropped. frostmoln_mysql_instance requires a VPC and subnet — vpc_id is synthesized as a placeholder and subnet_id comes from delegated_subnet_id when present. Backup retention is platform-managed. Verify the version is offered on the platform.

AttributeOutcomeNotes
nameMapped to name
versionMapped to versionVerify the version is offered on the platform (fm database mysql version list).
sku_nameConverted to flavor_idThe VM-size component of the SKU is parsed for its vCPUs and RAM and reported; flavor_id is left as an FM-TODO to set from fm database flavor list.
storageConverted to storage_gbThe storage block's size_gb becomes storage_gb; IOPS and auto-grow are platform-managed.
high_availabilityNot carried overhigh availability is available for PostgreSQL only; the API rejects a MySQL instance created with ha_enabled, so emitting it would produce HCL that fails at apply.
delegated_subnet_idMapped to subnet_id
administrator_loginNot carried overthe admin account is platform-issued.
administrator_passwordNot carried overthe admin password is platform-issued.
backup_retention_daysNot carried overAzure's 1-35 day retention differs from the platform's 35-90 day range; set backup_retention_days manually.
geo_redundant_backup_enabledNot carried overbackups are platform-managed.
zoneNot carried overthe platform picks the zone.
maintenance_windowNot carried overmaintenance windows are platform-managed.
private_dns_zone_idNot carried overprivate DNS integration is platform-managed.
customer_managed_keyManual action requiredcustomer-managed encryption keys have no equivalent.
tagsConvertedTags become frostmoln tags (at most 32 per resource, your tenant's default tags included).
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
locationNot needed on Frostmolnthe platform is single-region; the default region applies.
  • The converter adds version automatically. frostmoln_mysql_instance requires a version; set it to an offered version (fm database mysql version list).
  • The converter adds flavor_id automatically. frostmoln_mysql_instance requires a flavor; set flavor_id to a platform flavor (see fm database flavor list).
  • The converter adds storage_gb automatically. frostmoln_mysql_instance requires storage_gb; set it to the desired size in GiB.
  • The converter adds vpc_id automatically. frostmoln_mysql_instance requires a VPC; Azure servers have no VPC concept — set vpc_id to the converted frostmoln_vpc id.
  • The converter adds subnet_id automatically. frostmoln_mysql_instance requires a subnet; set subnet_id to the converted frostmoln_subnet id (delegated_subnet_id maps it when present).

azurerm_network_interface → frostmoln_instance — partially supported ​

Network interfaces are absorbed into the virtual machine that references them: the interface disappears, its subnet becomes the instance's subnet_id, and a referenced public IP becomes a frostmoln_public_ip bound to the instance.

Only an interface referenced by exactly one azurerm_linux_virtual_machine or azurerm_windows_virtual_machine (via network_interface_ids) and carrying a single ip_configuration with a subnet_id can be merged. A standalone interface has no equivalent — attach the instance via subnet_id. Static private IPs, multiple ip_configuration blocks, accelerated networking, and DNS servers have no equivalent. Generating the ip_configuration with a dynamic block also refuses the merge: the converter cannot read which subnet it selects, how many configurations it declares, or which public IPs they bind. An interface using count or for_each cannot be merged either. Every refusal marks the INSTANCE with a blocker for the subnet_id it never received, because frostmoln_instance.subnet_id is optional and an instance emitted without one would otherwise apply onto whichever network the platform picks.

AttributeOutcomeNotes
nameNot needed on Frostmolnthe interface is absorbed; the instance carries its own name.
ip_configurationConvertedsubnet_id becomes the instance's subnet_id; a referenced public_ip_address_id binds the converted frostmoln_public_ip to the instance; a static private_ip_address is dropped (the platform assigns one).
locationNot needed on Frostmolnthe interface is absorbed; the platform picks the zone.
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
dns_serversManual action requiredcustom DNS servers on the interface have no equivalent; set dns_servers on the subnet instead.
accelerated_networking_enabled (also matches enable_accelerated_networking)Not carried overaccelerated networking is platform-managed.
ip_forwarding_enabled (also matches enable_ip_forwarding)Not carried overNot carried over: forwarding is not a flag here, and no Azure route table converts. Write a frostmoln_vpc_route naming this interface's address as the next hop -- this is manual -- and read the cost in the VPC routes documentation first: that route also lets the interface source addresses it does not own.
tagsNot carried overtags belong on the instance.

azurerm_network_security_group → frostmoln_security_group — partially supported ​

Network security groups map to frostmoln_security_group; inline security_rule blocks are split into one frostmoln_security_group_rule each.

The platform security model is ALLOW-ONLY — a Deny rule has no equivalent and BLOCKS the conversion (silently dropping it would open the traffic it denies). A rule that cannot be expressed as a single allow rule (multiple address prefixes or port ranges, a restricted self side, a non-wildcard source port) also blocks — a frostmoln security-group rule filters the DESTINATION port only. Azure NSGs and Frostmoln security groups are both stateful, so such a rule was never return-traffic plumbing: it is a real source-port filter with no equivalent. Rewrite it against the destination port the traffic reaches (the source-port numbers are NOT reusable as destination ports), or keep the restriction at another layer. Inbound maps to ingress and Outbound to egress; the source (inbound) or destination (outbound) address prefix becomes remote_cidr; */Internet becomes 0.0.0.0/0. Rule priorities are dropped (the platform does not order rules). Security groups have no VPC association on Azure, so vpc_id is left unset. EGRESS DEFAULTS MATCH THE SOURCE - DO NOT "FIX" THEM: every Frostmoln security group is created with allow-all egress (the platform adds one IPv4 and one IPv6 allow-any egress rule to every new group and removes neither), and converted egress rules ADD to those defaults rather than replacing them, but an Azure NSG allows all outbound by default (AllowVnetOutBound and AllowInternetOutBound, which cannot be removed) and narrows it only with a Deny rule, which this converter always blocks - a Deny rule never becomes an output rule. So for an NSG whose outbound rules are all plain Allow, allow-all egress MATCHES the posture the source had and the additive platform default widens nothing. An NSG that DID declare an outbound Deny is reported as WIDENED instead: that rule blocked, so the restriction it expressed is absent from the output. Watch for a standalone azurerm_network_security_rule too - it blocks as its own resource, and an outbound Deny there also leaves the converted group wider than the source. Do NOT delete the two default egress rules unless you are deliberately tightening beyond the source: the group's egress would then be exactly the egress rules converted from this NSG, and where it declared none that denies ALL outbound traffic. ADDRESS FAMILY: a frostmoln_security_group_rule has no ether_type field, so every rule the platform creates is IPv4 and no converted rule can be IPv6. An inline security_rule whose address prefix is an IPv6 address or CIDR therefore BLOCKS - the rule is left unconverted rather than emitted as one that is rejected when it is applied - and the group is missing exactly that rule. Do NOT translate an IPv6 range to an IPv4 one, it selects different hosts. The wildcard prefixes are not equivalent to each other here: * covers both families, so 0.0.0.0/0 is strictly narrower, but Internet means the space OUTSIDE the virtual network, so converting it NARROWS on address family and WIDENS on address range at the same time - 0.0.0.0/0 includes the VPC's own ranges that the Internet tag excluded. Both are kept with a review finding rather than blocked, because no conversion can express the IPv6 half and a hand-rewrite lands on the same value; check the review finding on each.

AttributeOutcomeNotes
nameMapped to name
locationNot needed on Frostmolnthe platform is single-region; the default region applies.
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsConverted

Inline security_rule blocks are split into frostmoln_security_group_rule resources:

Block attributeOutcomeNotes
accessConverted
directionConverted
protocolConverted
source_address_prefixConverted to remote_cidr
destination_address_prefixConverted to remote_cidr
destination_port_rangeConverted to port_range_min
destination_port_rangeConverted to port_range_max
descriptionMapped to description
source_address_prefixesManual action requiredmultiple source prefixes cannot be one rule; create one frostmoln_security_group_rule per CIDR.
destination_address_prefixesManual action requiredmultiple destination prefixes cannot be one rule; create one frostmoln_security_group_rule per CIDR.
destination_port_rangesManual action requiredmultiple port ranges cannot be one rule; create one frostmoln_security_group_rule per range.
source_port_rangesManual action requireda frostmoln security-group rule filters the DESTINATION port only, so a source-port restriction cannot be carried. Azure NSGs and Frostmoln security groups are both stateful, so such a rule was never return-traffic plumbing: it is a real source-port filter with no equivalent. Rewrite it against the destination port the traffic reaches (the source-port numbers are NOT reusable as destination ports), or keep the restriction at another layer.
source_port_rangeNot needed on Frostmolnconsumed by the access guard: only the unrestricted wildcard "*" converts, and a real source-port restriction blocks the whole rule.
priorityNot carried overthe platform does not order rules; there is no priority.
nameNot carried oversecurity-group rules have no name.
  • The converter adds name automatically. frostmoln_security_group requires a name; it is taken from the Name tag or the resource address.

azurerm_network_security_rule → frostmoln_security_group_rule — partially supported ​

Standalone security rules map to frostmoln_security_group_rule; Deny rules and unexpressible shapes block the conversion (the platform is allow-only).

A Deny rule has no equivalent and BLOCKS the whole rule — silently converting it would open the traffic it denies. Inbound maps to ingress and Outbound to egress; the source (inbound) or destination (outbound) address prefix becomes remote_cidr; */Internet becomes 0.0.0.0/0. Multiple address prefixes or port ranges, a restricted self side, and non-wildcard source ports also block — a frostmoln security-group rule filters the DESTINATION port only. Azure NSGs and Frostmoln security groups are both stateful, so such a rule was never return-traffic plumbing: it is a real source-port filter with no equivalent. Rewrite it against the destination port the traffic reaches (the source-port numbers are NOT reusable as destination ports), or keep the restriction at another layer. Rule priority is dropped. The wildcard destination port * becomes the full 1-65535 range. ADDRESS FAMILY: a frostmoln_security_group_rule has no ether_type field, so every rule the platform creates is IPv4 and no converted rule can be IPv6. An address prefix that is an IPv6 address or CIDR therefore BLOCKS THE WHOLE RULE rather than being emitted as a rule that is rejected when it is applied - do NOT translate an IPv6 range to an IPv4 one, it selects different hosts. The two wildcard prefixes are NOT equivalent: * covers both families and every address, so 0.0.0.0/0 is strictly narrower, but Internet means the space OUTSIDE the virtual network, so converting it NARROWS on address family and WIDENS on address range at the same time - 0.0.0.0/0 includes the VPC's own ranges the Internet tag excluded. Both are kept rather than blocked, because no conversion can express the IPv6 half and a hand-rewrite lands on the same value; each carries its own review finding saying which happened.

AttributeOutcomeNotes
accessConverted
directionConverted
protocolConverted
source_address_prefixConverted to remote_cidr
destination_address_prefixConverted to remote_cidr
destination_port_rangeConverted to port_range_min
network_security_group_nameConverted to security_group_idA direct azurerm_network_security_group reference is rewritten to the converted frostmoln_security_group id; otherwise set security_group_id manually.
source_address_prefixesManual action requiredmultiple source prefixes cannot be one rule; create one frostmoln_security_group_rule per CIDR.
destination_address_prefixesManual action requiredmultiple destination prefixes cannot be one rule; create one frostmoln_security_group_rule per CIDR.
destination_port_rangesManual action requiredmultiple port ranges cannot be one rule; create one frostmoln_security_group_rule per range.
source_port_rangesManual action requireda frostmoln security-group rule filters the DESTINATION port only, so a source-port restriction cannot be carried. Azure NSGs and Frostmoln security groups are both stateful, so such a rule was never return-traffic plumbing: it is a real source-port filter with no equivalent. Rewrite it against the destination port the traffic reaches (the source-port numbers are NOT reusable as destination ports), or keep the restriction at another layer.
source_port_rangeNot needed on Frostmolnconsumed by the access guard: only the unrestricted wildcard "*" converts, and a real source-port restriction blocks the whole rule.
priorityNot carried overthe platform does not order rules; there is no priority.
descriptionMapped to description
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
  • The converter adds port_range_max automatically. The upper bound of the destination port range (equal to port_range_min for a single port).

azurerm_postgresql_flexible_server → frostmoln_postgres_instance — partially supported ​

PostgreSQL Flexible Servers map to frostmoln_postgres_instance; the SKU's size is reported and flavor_id is left for you to set, storage_mb becomes storage_gb, and a high_availability block becomes ha_enabled.

A managed database draws flavor_id from the managed-database flavor catalog, which is separate from the compute one — so the converter reports the source SKU's vCPUs and RAM and leaves flavor_id as an FM-TODO for you to set from fm database flavor list. Administrator credentials are platform-issued and dropped. frostmoln_postgres_instance requires a VPC and subnet — vpc_id is synthesized as a placeholder (Azure servers have no VPC concept) and subnet_id comes from delegated_subnet_id when present. Azure's 7-35 day backup retention differs from the platform's 35-90 day range, so backup_retention_days is dropped — set it manually. Verify the version is offered on the platform.

AttributeOutcomeNotes
nameMapped to name
versionMapped to versionVerify the version is offered on the platform (fm database postgres version list).
sku_nameConverted to flavor_idThe VM-size component of the SKU is parsed for its vCPUs and RAM and reported; flavor_id is left as an FM-TODO to set from fm database flavor list.
storage_mbConverted to storage_gbAzure sizes storage in MiB; storage_gb is storage_mb / 1024.
high_availabilityConverted to ha_enabledThe presence of a high_availability block becomes ha_enabled = true.
delegated_subnet_idMapped to subnet_id
administrator_loginNot carried overthe admin account is platform-issued.
administrator_passwordNot carried overthe admin password is platform-issued.
backup_retention_daysNot carried overAzure's 7-35 day retention differs from the platform's 35-90 day range; set backup_retention_days manually.
geo_redundant_backup_enabledNot carried overbackups are platform-managed.
storage_tierNot carried overstorage performance tiers are platform-managed.
storage_typeNot carried overstorage performance tiers are platform-managed.
storage_iopsNot carried overstorage performance tiers are platform-managed; IOPS are not tunable.
storage_throughputNot carried overstorage performance tiers are platform-managed; throughput is not tunable.
auto_grow_enabledNot carried overstorage auto-grow is platform-managed.
zoneNot carried overthe platform picks the zone.
maintenance_windowNot carried overmaintenance windows are platform-managed.
authenticationManual action requiredauthentication modes (Azure AD, password policy) have no equivalent; access is platform-managed.
private_dns_zone_idNot carried overprivate DNS integration is platform-managed.
customer_managed_keyManual action requiredcustomer-managed encryption keys have no equivalent.
tagsConvertedTags become frostmoln tags (at most 32 per resource, your tenant's default tags included).
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
locationNot needed on Frostmolnthe platform is single-region; the default region applies.
  • The converter adds version automatically. frostmoln_postgres_instance requires a version; set it to an offered version (fm database postgres version list).
  • The converter adds flavor_id automatically. frostmoln_postgres_instance requires a flavor; set flavor_id to a platform flavor (see fm database flavor list).
  • The converter adds storage_gb automatically. frostmoln_postgres_instance requires storage_gb; set it to the desired size in GiB.
  • The converter adds vpc_id automatically. frostmoln_postgres_instance requires a VPC; Azure servers have no VPC concept — set vpc_id to the converted frostmoln_vpc id.
  • The converter adds subnet_id automatically. frostmoln_postgres_instance requires a subnet; set subnet_id to the converted frostmoln_subnet id (delegated_subnet_id maps it when present).

azurerm_public_ip → frostmoln_public_ip — partially supported ​

Public IPs map to frostmoln_public_ip; the SKU and allocation method are platform-managed and a specific address cannot be requested.

When a network interface references this public IP, the absorbing virtual machine binds it via instance_id. A DNS label (domain_name_label) has no equivalent — create a frostmoln_dns_record instead. Static allocation and SKU tiers are platform-managed.

AttributeOutcomeNotes
allocation_methodNot needed on Frostmolnallocation is platform-managed; the platform assigns the address.
skuNot needed on Frostmolnthere is no SKU tier; all public IPs are equivalent.
sku_tierNot needed on Frostmolnthere is no SKU tier; all public IPs are equivalent.
locationNot needed on Frostmolnthe platform is single-region; the default region applies.
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsConverted
domain_name_labelManual action requiredDNS labels have no equivalent; create a frostmoln_dns_record pointing at the address.
ip_versionManual action requiredIPv6 addressing is not supported yet.
zonesNot carried overavailability zones are platform-managed.
public_ip_prefix_idManual action requiredpublic IP prefixes have no equivalent; the platform allocates the address.
idle_timeout_in_minutesNot carried overidle timeouts are platform-managed.
reverse_fqdnManual action requiredreverse DNS has no equivalent.
ip_tagsNot carried overIP tags have no equivalent.

azurerm_redis_cache → frostmoln_redis_instance — partially supported ​

Redis caches map to frostmoln_redis_instance; the Azure tier model (sku/capacity/family) does not map to a platform flavor, so flavor_id is a placeholder to set from fm cache flavor list.

Azure sizes Redis by SKU tier + capacity rather than vCPU/RAM, so flavor_id, vpc_id, and subnet_id are synthesized as placeholders. The redis_configuration block is dropped — set eviction_policy (from maxmemory_policy) and persistence_mode (rdb/aof) on the instance manually. Verify the version is offered on the platform. The managed cache serves the cache protocol in PLAINTEXT on port 6379 at its private address in your VPC and has no TLS listener, whatever the source configuration asked for — the AUTH password therefore also crosses the wire in cleartext, as do the keys and values themselves; and with no TLS there is no integrity protection either, so anything on the network path inside your VPC can read and modify the traffic. Azure defaults caches to TLS-only, so this differs from the source even when the configuration says nothing about it.

AttributeOutcomeNotes
nameMapped to name
redis_versionMapped to versionVerify the version is offered on the platform (fm cache redis version list).
subnet_idMapped to subnet_idOnly Premium caches attach to a subnet; verify it references the converted frostmoln_subnet.
capacityNot carried overthe Azure capacity tier does not map to a platform flavor; set flavor_id manually.
familyNot carried overthe Azure family (C/P) does not map to a platform flavor; set flavor_id manually.
sku_nameNot carried overthe Azure SKU tier does not map to a platform flavor; set flavor_id manually.
redis_configurationNot carried overset eviction_policy (from maxmemory_policy) and persistence_mode (rdb/aof) on the instance manually.
non_ssl_port_enabled (also matches enable_non_ssl_port)Manual action requiredthe managed cache has no TLS listener — it serves the cache protocol in plaintext on port 6379 at its private address in your VPC, so a TLS-only posture cannot be reproduced and the AUTH password crosses the wire in cleartext; review your transport-security requirement before migrating.
minimum_tls_versionManual action requiredthere is no TLS listener to set a minimum version on — the managed cache serves plaintext on port 6379.
patch_scheduleNot carried overpatching is platform-managed.
zonesNot carried overthe platform picks the zone.
private_static_ip_addressManual action requiredstatic private IPs are not supported; the platform assigns one.
public_network_access_enabledNot carried overnetwork access is platform-managed.
tagsConvertedTags become frostmoln tags (at most 32 per resource, your tenant's default tags included).
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
locationNot needed on Frostmolnthe platform is single-region; the default region applies.
  • The converter adds version automatically. frostmoln_redis_instance requires a version; set it to an offered version (fm cache redis version list).
  • The converter adds flavor_id automatically. frostmoln_redis_instance requires a flavor; the Azure tier does not map — set flavor_id (see fm cache flavor list).
  • The converter adds vpc_id automatically. frostmoln_redis_instance requires a VPC; set vpc_id to the converted frostmoln_vpc id.
  • The converter adds subnet_id automatically. frostmoln_redis_instance requires a subnet; set subnet_id to the converted frostmoln_subnet id.

azurerm_snapshot → frostmoln_snapshot — partially supported ​

Disk snapshots map to frostmoln_snapshot; the source managed disk becomes volume_id.

Only a snapshot copied from a managed disk (create_option Copy, source_resource_id) converts cleanly — a blob-import snapshot has no equivalent. A required snapshot name is synthesized from the Name tag or the resource address.

AttributeOutcomeNotes
nameMapped to name
source_resource_idConverted to volume_idverify this references the source managed disk (converted to a frostmoln_volume).
create_optionNot needed on Frostmolnthe platform infers snapshot semantics from volume_id.
source_uriManual action requiredimporting a snapshot from a blob URI has no equivalent.
storage_account_idManual action requiredblob-backed snapshots have no equivalent.
disk_size_gbNot carried overthe snapshot size is derived from the source volume.
incremental_enabledNot carried overincremental snapshots are platform-managed.
encryption_settingsManual action requiredcustomer-managed encryption keys have no equivalent; snapshots are always encrypted at rest with platform-managed keys.
disk_encryption_set_id (retired by the provider)Manual action requiredcustomer-managed encryption keys have no equivalent.
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
locationNot needed on Frostmolnthe platform picks the zone.
tagsConverted
  • The converter adds name automatically. frostmoln_snapshot requires a name; it is taken from the Name tag or the resource address.
  • The converter adds volume_id automatically. frostmoln_snapshot requires a source volume; set volume_id to the converted frostmoln_volume id.

azurerm_ssh_public_key → frostmoln_ssh_key — supported ​

SSH public keys map directly to frostmoln_ssh_key.

The key name becomes the resource name; tags are not supported on SSH keys.

AttributeOutcomeNotes
nameMapped to name
public_keyMapped to public_key
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
locationNot needed on Frostmolnthe platform is single-region; the default region applies.
tagsNot carried overfrostmoln_ssh_key has no tags.

azurerm_storage_container → frostmoln_bucket — partially supported ​

Storage containers map to frostmoln_bucket; the container name becomes the bucket name.

Bucket names are globally unique on the platform — if the container name collides, prefix it with the storage account name. Buckets are private and stay private: a public container_access_type has no equivalent. The bucket API does carry an acl field and will accept and echo back public-read, but it is inert - it is kept as bucket metadata and never applied to the storage backend - and there is no bucket-policy API at all. Every issued S3 credential is additionally denied s3:PutBucketAcl and s3:PutBucketPolicy, so anonymous read cannot be re-created by any route. Re-grant access with a frostmoln_s3_credential scoped to the bucket (allowed_buckets, optionally ip_whitelist, and allowed_actions - which takes a fixed catalogue of S3 actions, so leave it out to grant them all). For a browser, hand out a presigned URL instead of a public object, remembering it is issued through the API or portal rather than Terraform and lasts at most an hour. Object metadata and immutability policies are dropped.

AttributeOutcomeNotes
nameMapped to nameThe container name becomes the bucket name; prefix it with the storage account name if it collides globally.
storage_account_id (also matches storage_account_name)Not carried overthere is no storage-account container; the bucket is tenant-scoped — fold the storage account's name into the bucket name if needed for uniqueness.
container_access_typeManual action requiredbuckets are private; a public container has no equivalent — the bucket's acl field is inert and there is no bucket policy, so re-grant access with a frostmoln_s3_credential scoped to the bucket, or hand out a presigned URL.
metadataNot carried overcontainer metadata has no equivalent.
default_encryption_scopeNot needed on Frostmolnobjects are always encrypted at rest; encryption is not configurable.

data.azurerm_subnet → frostmoln_subnet — partially supported ​

The azurerm_subnet data source maps to the frostmoln_subnet data source, which selects a subnet by id or name (optionally narrowed by vpc_id); resource-group scoping has no equivalent.

frostmoln_subnet looks up a subnet by id or name. virtual_network_name narrows the lookup by VPC — a direct azurerm_virtual_network reference is rewritten to the converted VPC id, otherwise set vpc_id manually.

AttributeOutcomeNotes
nameMapped to name
virtual_network_nameConverted to vpc_idA direct azurerm_virtual_network reference is rewritten to the converted frostmoln_vpc id.
resource_group_nameManual action requiredresource-group scoping has no equivalent; look the subnet up by name.

azurerm_subnet → frostmoln_subnet — partially supported ​

Subnets map to frostmoln_subnet; the first address prefix becomes the subnet CIDR and the parent virtual network becomes vpc_id.

frostmoln_subnet takes a single CIDR — a subnet with more than one address_prefixes keeps only the first and flags the rest for review. virtual_network_name references the network by name; a direct azurerm_virtual_network reference is rewritten to the converted VPC id, otherwise set vpc_id manually. Service endpoints, delegations, and private-endpoint policies are dropped (the platform manages them).

AttributeOutcomeNotes
nameMapped to name
address_prefixesConverted to cidrThe first address prefix becomes cidr; additional ranges are flagged for review.
virtual_network_nameConverted to vpc_idA direct azurerm_virtual_network reference is rewritten to the converted frostmoln_vpc id; otherwise set vpc_id manually.
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
service_endpoint (also matches service_endpoints)Not carried overservice endpoints have no equivalent; the platform routes service traffic directly.
service_endpoint_policy_idsNot carried overservice-endpoint policies have no equivalent.
delegationNot carried oversubnet delegations have no equivalent; managed services attach to subnets directly.
private_endpoint_network_policies (also matches private_endpoint_network_policies_enabled)Not carried overprivate-endpoint network policies have no equivalent.
private_link_service_network_policies_enabledNot carried overprivate-link network policies have no equivalent.
default_outbound_access_enabledNot carried overthere is no default outbound access to enable or disable, and no per-subnet switch for it: a Frostmoln VPC has NO outbound path at all unless a frostmoln_gateway is attached to the VPC (see azurerm_nat_gateway for what to add), and one attached there serves every subnet.
  • The converter adds name automatically. frostmoln_subnet requires a name; it is taken from the Name tag or the resource address.

azurerm_virtual_machine_data_disk_attachment → frostmoln_volume_attachment — supported ​

Data disk attachments map to frostmoln_volume_attachment; the managed disk becomes volume_id and the VM becomes instance_id.

The LUN, caching mode, and write-accelerator flag are platform-managed and dropped. The device path is assigned by the platform.

AttributeOutcomeNotes
managed_disk_idMapped to volume_id
virtual_machine_idMapped to instance_id
lunNot carried overthe logical unit number is platform-assigned; the device path is exported.
cachingNot carried overdisk caching is platform-managed.
write_accelerator_enabledNot carried overwrite acceleration is platform-managed.
create_optionNot needed on Frostmolnattach semantics are platform-managed.

data.azurerm_virtual_network → frostmoln_vpc — partially supported ​

The azurerm_virtual_network data source maps to the frostmoln_vpc data source, which selects a VPC by id or name; resource-group scoping has no equivalent.

frostmoln_vpc looks up a VPC by id or name. The address_space export is a list on Azure but a single cidr on the platform — references to address_space break; use cidr.

AttributeOutcomeNotes
nameMapped to name
resource_group_nameManual action requiredresource-group scoping has no equivalent; look the VPC up by name.

azurerm_virtual_network → frostmoln_vpc — partially supported ​

Virtual networks map to frostmoln_vpc; the first address space becomes the VPC CIDR.

frostmoln_vpc takes a single CIDR — a virtual network with more than one address_space keeps only the first and flags the rest for review (recreate additional ranges as separate resources). DNS servers, DDoS plans, and peerings have no equivalent at the VPC level. The converted VPC has NO outbound internet access on its own — Azure's default outbound access has no counterpart: outbound is an explicit frostmoln_gateway (one per VPC, egressing from an address the platform draws for it; name a Public IP of your own with public_ip_id where a partner allow-lists your source address, and that address is billed as a Public IP), and without it the VPC also has no DNS resolution and no managed-service control plane, because those are reached over routes that exist only while the egress path does (a managed database, cache or broker deployed INTO the VPC still answers on its private address; reaching it by name needs DNS).

AttributeOutcomeNotes
nameMapped to name
address_spaceConverted to cidrThe first address space becomes cidr; additional ranges are flagged for review.
locationNot needed on Frostmolnthe platform is single-region; the default region applies.
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsConverted
dns_serversManual action requiredcustom DNS servers have no equivalent at the VPC level; set dns_servers on the subnet.
ddos_protection_planManual action requiredDDoS protection plans have no equivalent; the platform protects the edge.
subnetManual action requiredinline subnet blocks are not supported; recreate them as frostmoln_subnet resources.
bgp_communityManual action requiredBGP communities have no equivalent.
flow_timeout_in_minutesNot carried overflow timeouts are platform-managed.
edge_zoneManual action requirededge zones have no equivalent.
  • The converter adds name automatically. frostmoln_vpc requires a name; it is taken from the Name tag or the resource address.

azurerm_web_application_firewall_policy → frostmoln_appgw_waf_policy — partially supported ​

A firewall policy becomes a frostmoln_appgw_waf_policy together with the frostmoln_appgw_waf_policy_attachment that puts it somewhere and the frostmoln_appgw_waf_policy_publication that makes the gateway inspect with it; its managed-rule overrides and custom rules do not convert, and the policy is emitted in detect mode — refusing nothing — whatever the source said.

AUTHORING A POLICY IS NOT ENFORCING IT, and the gap is invisible in a plan. A frostmoln_appgw_waf_policy records what you want; what the gateway inspects with is the last PUBLISHED version. A policy whose rules are written but never published inspects nothing, and a policy that is never attached is not applied anywhere — terraform apply succeeds in both cases. That is why this conversion emits three resources rather than one: the policy, a frostmoln_appgw_waf_policy_attachment, and a frostmoln_appgw_waf_policy_publication. Keep all three. And a published version still only reaches the appliance on the gateway's next frostmoln_appgw_config_apply.

THE CONVERTED POLICY IS EMITTED IN detect MODE, WHATEVER THE SOURCE SAID, INCLUDING Prevention. Detect runs the ruleset and records what it WOULD have refused, refusing nothing. That is deliberate and it is not the safe-looking default it resembles: the converted policy inspects with the PLATFORM's managed ruleset, and your managed-rule overrides and exclusions do not convert with it, so a converted policy put straight into blocking mode would refuse legitimate traffic your Azure policy allowed. The cost is that a Prevention-mode source applies cleanly and is NOT protected until you move it. The generated mode carries an FM-TODO saying so, and mode is the one FM-TODO in this conversion that is a valid, server-accepted value — every other one fails the apply until you resolve it, and this one does not.

Do not lean on the publication's dry-run to catch that for you. It refuses to publish while more than max_newly_blocked requests IN ITS SAMPLE would newly be refused, and that limit defaults to 0 — but a gateway that has just been created has served nothing, so the sample is empty and the check passes vacuously. It becomes a real gate only once the gateway has seen real traffic, which is also when it is worth moving to block.

One combination the converter can produce and nothing else warns about: if you follow the scope FM-TODO to overlay and leave mode at detect, you have explicitly switched blocking OFF for that one listener or route while the gateway policy blocks everywhere else. An overlay's cautious setting is inherit, not detect.

The attachment and the publication are generated from the managed_rules block — the one block Azure requires exactly once, used as the anchor for these one-per-policy resources rather than as a translation of the managed rules themselves, which do not convert.

Scope is an FM-TODO because the source does not answer it. A gateway-scoped policy attaches to the whole gateway and carries the managed ruleset along with the dials that tune it — paranoia_level, anomaly_score_threshold, managed_ruleset_version. An overlay-scoped policy attaches to ONE listener or ONE route, is compiled WITHOUT the managed ruleset, and carries only your own rules; those dials do not apply to it and the server refuses them there. Where this policy was named by an application gateway's firewall_policy_id, it is gateway-scoped; where it was named by a single listener or path rule, it is an overlay, and the attachment then needs that listener_id (and route_id). An overlay defaults to mode = "inherit", which is not the cautious setting: under a blocking gateway policy an inheriting overlay blocks. Read effective_mode, never mode.

Rules do not convert. A custom rule becomes a frostmoln_appgw_waf_rule with kind = "builder" and a builder_json condition set; a managed rule turned off becomes one with kind = "managedOverride"; an exclusion becomes a frostmoln_appgw_waf_exclusion. Each lands on the policy's DRAFT, so each needs its revision fed into the publication's rule_revisions map before it is enforced. Rate-limit rules have no equivalent here — per-source rate limiting is rate_limit_rps and rate_limit_burst on a frostmoln_appgw_listener.

AttributeOutcomeNotes
nameMapped to name
locationNot needed on Frostmolnthe platform is single-region; the default region applies.
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsConvertedTags become the policy's frostmoln tags (at most 32, your tenant's default tags included).
policy_settingsManual action requiredthe settings live on the policy itself. mode does NOT carry over: the converter emits detect whatever this said, including Prevention, because your managed-rule overrides and exclusions do not convert and blocking with an untuned ruleset would refuse legitimate traffic — so a Prevention-mode policy converts to one that inspects and refuses nothing until you move it deliberately. max_request_body_size_in_kb becomes request_body_limit_bytes in BYTES, between 4096 and 1048576 with a default of 40960 — a body over it is refused 413 rather than passed uninspected, so it is also the largest request your application receives. The upload limits, the log-scrubbing rules and the JS-challenge settings have no equivalent.
custom_rulesManual action requiredeach custom rule becomes a frostmoln_appgw_waf_rule with kind = "builder" and a builder_json condition set, which lands on the policy's DRAFT and inspects nothing until a frostmoln_appgw_waf_policy_publication publishes it — feed each rule's revision into that publication's rule_revisions. Rate-limit rules have no equivalent: per-source rate limiting is rate_limit_rps and rate_limit_burst on a frostmoln_appgw_listener.

Inline managed_rules blocks are split into frostmoln_appgw_waf_policy_attachment resources:

Block attributeOutcomeNotes
managed_rule_setManual action requiredthere is one managed ruleset and it is the platform's — managed_ruleset_version is read, not written, and paranoia_level and anomaly_score_threshold are how it is tuned (on a gateway-scoped policy only). A rule_group_override becomes a frostmoln_appgw_waf_rule with kind = "managedOverride" and managed_action = "disable", one per rule; prefer a frostmoln_appgw_waf_exclusion where the rule is only a false positive on one field.
exclusionManual action requiredeach exclusion becomes a frostmoln_appgw_waf_exclusion with variable, selector and an optional path_pattern, targeting one managed rule (target_secrule_id) or one tag (target_tag). It lands on the policy's DRAFT and takes effect only once a frostmoln_appgw_waf_policy_publication publishes it.

Each split also creates a frostmoln_appgw_waf_policy_publication resource.

  • The converter adds gateway_id automatically. frostmoln_appgw_waf_policy requires gateway_id; set it to the converted frostmoln_application_gateway this policy protects.
  • The converter adds scope automatically. set scope to gateway where an application gateway's firewall_policy_id named this policy, or to overlay where a single listener or path rule did. An overlay is compiled WITHOUT the managed ruleset and the managed-ruleset dials are refused on it; changing scope later replaces the policy.
  • The converter adds mode automatically. 🔴 DETECT REFUSES NOTHING, AND THIS IS THE ONE PLACEHOLDER IN THIS CONVERSION THAT APPLIES CLEANLY. The others are visibly fake ("YOUR-APPLICATION-GATEWAY-ID") and fail until you resolve them; this is a real, server-accepted value, so a source policy in Prevention mode converts, plans, applies, attaches, publishes -- and protects nothing, with a green plan the whole way. It is detect rather than a translation of policy_settings.mode because your managed-rule overrides and exclusions do not convert, and blocking with an untuned ruleset would refuse legitimate traffic. Watch the decision log under real traffic, add a frostmoln_appgw_waf_exclusion or a managedOverride frostmoln_appgw_waf_rule for each false positive, publish, and THEN set mode = "block".

azurerm_windows_virtual_machine → frostmoln_instance — partially supported ​

Windows VMs map to frostmoln_instance: the VM size is capability-matched to a flavor, the source image becomes a frostmoln_image lookup, and the referenced network interface is absorbed (its subnet becomes subnet_id and its public IP becomes a frostmoln_public_ip).

Conversion depends on an equivalent Windows image being available in the platform catalog (fm compute image list). The admin password is platform-issued and dropped. subnet_id is contributed by the absorbed azurerm_network_interface. Network security groups are NOT associated automatically; set security_groups to the converted frostmoln_security_group ids. os_disk sizing is flavor/image-determined and dropped.

AttributeOutcomeNotes
nameMapped to name
sizeConverted to flavor_idCapability-matched against the platform flavor catalog and emitted as a frostmoln_flavor data source lookup by name.
source_image_idConverted to image_idA marketplace/shared image id becomes a frostmoln_image data source with a reviewed placeholder name.
source_image_referenceConverted to image_idpublisher/offer/sku are matched against the platform image catalog when possible, otherwise a reviewed placeholder is emitted.
custom_dataConverted to user_dataAzure custom_data is base64-encoded; it is decoded into plain-text user_data.
network_interface_idsNot needed on Frostmolnthe network interface is absorbed into the instance; subnet_id comes from the interface.
locationConvertedThe platform picks the zone; set zone to a zone id to pin one.
zoneNot carried overAzure availability-zone numbers do not map to platform zones; the platform picks the zone — set zone to pin one.
resource_group_nameNot needed on Frostmolnthere is no resource-group concept; resources are tenant-scoped.
tagsConverted
admin_usernameNot carried overthe admin account is platform-managed.
admin_passwordNot carried overpasswords are platform-issued; retrieve console access via the platform.
computer_nameNot carried overthe hostname is platform-managed.
os_diskNot carried overthe root disk is determined by the flavor and image; add a frostmoln_volume for extra storage.
identityManual action requiredmanaged identities have no equivalent; grant access with scoped API credentials.
boot_diagnosticsNot carried overboot diagnostics are platform-managed.
availability_set_idManual action requiredavailability sets have no equivalent.
proximity_placement_group_idManual action requiredproximity placement groups have no equivalent.
priorityManual action requiredspot/low-priority VMs have no equivalent; instances are always regular.
eviction_policyManual action requiredspot eviction policies have no equivalent.
patch_modeNot carried overOS patching is customer-managed inside the instance.
secretManual action requiredVM secret injection has no equivalent; use the secrets service.
winrm_listenerNot carried overWinRM listeners are configured inside the instance.
additional_capabilitiesNot carried overadditional capabilities have no equivalent.
planNot carried overmarketplace plans have no equivalent.
  • The converter adds name automatically. frostmoln_instance requires a name; it is taken from the Name tag or the resource address.
  • The converter adds flavor_id automatically. frostmoln_instance requires a flavor; set flavor_id to a platform flavor (see fm compute flavor list).
  • The converter adds image_id automatically. frostmoln_instance requires an image; set image_id to a platform image (see fm compute image list).

Resources the converter does not convert ​

ResourceWhat happens
azurerm_application_insightsApplication Insights has no equivalent; there is no managed APM service to provision.
data.azurerm_client_configThere is no caller-identity data source; the tenant the provider operates in is provider configuration (the frostmoln provider's tenant_id, or FROSTMOLN_TENANT_ID), so replace references with that value.
azurerm_container_appContainer Apps have no equivalent; there is no managed serverless-container platform.
azurerm_container_registryFrostmoln has a managed container registry, but this resource does not convert: a registry is enabled once per tenant and nothing about it is configurable, so an Azure registry's arguments have nothing to map onto.
azurerm_cosmosdb_accountCosmos DB has no equivalent; the platform offers managed PostgreSQL, MySQL, Redis, and Valkey, but no globally-distributed multi-model NoSQL database.
azurerm_eventgrid_topicEvent Grid has no equivalent; there is no managed event-routing / pub-sub eventing service.
azurerm_eventhub_namespaceEvent Hubs has no equivalent; there is no managed high-throughput event-streaming service.
azurerm_federated_identity_credentialThe platform DOES federate Kubernetes workloads - frostmoln_workload_identity_binding is the counterpart - but nothing is emitted automatically, because this block carries none of the values the binding needs (cluster, namespace, service account, and a grant).
azurerm_firewallAzure Firewall has no equivalent — the platform offers security groups (frostmoln_security_group), not a managed stateful appliance — and where this firewall was also the network's way out, that half is a frostmoln_gateway you must add separately.
azurerm_key_vaultKey vaults have no container equivalent; secrets are stored flat, so each azurerm_key_vault_secret becomes a frostmoln_secret directly and the vault is removed. If this vault carries INLINE access_policy blocks, their permissions are removed with it and NOT converted — re-express each one as a standalone azurerm_key_vault_access_policy resource and re-run, or write the frostmoln_iam_policy by hand.
azurerm_key_vault_access_policyFrostmoln grants access from the principal side, not the vault side: the converter reads this resource's secret_permissions and writes a COMMENTED frostmoln_iam_policy candidate you review and uncomment, then attach with frostmoln_iam_policy_attachment to the api_key, workload_identity, or group that needs the secrets. Access policies written INLINE as access_policy blocks on azurerm_key_vault are not read - move them to this standalone resource before converting.
azurerm_linux_function_appFunction Apps have no equivalent; there is no serverless functions platform.
azurerm_linux_web_appFrostmoln has a managed webserver offer, but this resource does not convert: it serves static and PHP sites, so an App Service on another runtime has no counterpart and the rest is rewritten rather than translated.
azurerm_log_analytics_workspaceLog Analytics has no equivalent; the platform provides its own observability stack rather than a customer-provisioned logging workspace.
azurerm_logic_app_workflowLogic Apps have no equivalent; there is no managed workflow-orchestration service.
azurerm_mysql_flexible_databaseThe platform does not create logical databases inside a managed instance - a frostmoln_mysql_instance comes up with no application database at all, so you must create yours over the connection with SQL before pointing an app at it.
azurerm_mysql_flexible_server_active_directory_administratorManaged instances have no directory-backed authentication: the admin account on a frostmoln_mysql_instance is platform-issued, and database users are managed inside the database rather than mapped to platform identities.
azurerm_mysql_flexible_server_configurationServer parameters have no working equivalent yet - parameter groups exist as an API and frostmoln_mysql_instance accepts a parameter_group_id, but nothing applies a group to a running instance, so treat every setting here as unconverted and re-tune after migrating.
azurerm_mysql_flexible_server_firewall_ruleThere is no IP allow-list on a managed database: a frostmoln_mysql_instance is reachable from inside the VPC it is created in and nowhere else, so access is decided by placement (vpc_id, subnet_id) rather than by firewall rules.
azurerm_nat_gatewayA Frostmoln VPC reaches the internet only while a frostmoln_gateway is attached to it, and the converter must not decide for you whether that path egresses from a platform-drawn address or from a Public IP you own. A VPC may have at most ONE, so several NAT gateways serving one VPC collapse into a single gateway.
azurerm_nat_gateway_public_ip_associationA frostmoln_gateway carries ONE source address, named on the gateway itself (public_ip_id) rather than bound to it by a separate association resource.
azurerm_nat_gateway_public_ip_prefix_associationA frostmoln_gateway carries a single source address — the platform's, or one Public IP you own — and there is no public-IP-prefix concept to attach.
azurerm_network_interface_security_group_associationInterface-to-security-group associations are not merged automatically; set security_groups on the converted frostmoln_instance to the converted frostmoln_security_group id.
azurerm_postgresql_flexible_server_active_directory_administratorManaged instances have no directory-backed authentication: the admin account on a frostmoln_postgres_instance is platform-issued, and database roles are managed inside the database rather than mapped to platform identities.
azurerm_postgresql_flexible_server_configurationServer parameters have no working equivalent yet - parameter groups exist as an API and frostmoln_postgres_instance accepts a parameter_group_id, but nothing applies a group to a running instance, so treat every setting here as unconverted and re-tune after migrating.
azurerm_postgresql_flexible_server_databaseThe platform does not create logical databases inside a managed instance - a frostmoln_postgres_instance comes up with the stock postgres database plus one named after the platform-issued admin user, and any further database is created over the connection with SQL.
azurerm_postgresql_flexible_server_firewall_ruleThere is no IP allow-list on a managed database: a frostmoln_postgres_instance is reachable from inside the VPC it is created in and nowhere else, so access is decided by placement (vpc_id, subnet_id) rather than by firewall rules.
azurerm_private_dns_zoneThere are no private DNS zones on the platform: a frostmoln_dns_zone is always a public zone, delegated to the nameserver set the platform assigns it, so there is nothing for a private zone to convert to.
azurerm_private_dns_zone_virtual_network_linkA virtual-network link attaches a private DNS zone to a VNet, and the platform has no private DNS zones - so there is no link to convert either (see azurerm_private_dns_zone).
azurerm_private_endpointPrivate endpoints have no equivalent; managed services attach to your VPC subnet directly rather than through a private-link endpoint.
azurerm_resource_groupResource groups have no equivalent; Frostmoln resources are tenant-scoped, so the group is removed.
azurerm_role_assignmentFrostmoln has a fine-grained access-policy engine, but nothing is converted automatically: re-express the grant as a frostmoln_iam_policy composed from a frostmoln_iam_policy_document, and attach it with frostmoln_iam_policy_attachment to the api_key, workload identity, or group that needs it.
azurerm_servicebus_namespaceService Bus has no equivalent; the managed message broker speaks AMQP, but Service Bus semantics (topics/subscriptions/sessions/dead-lettering) do not map to broker queueing — the gap is the messaging model, not the protocol.
azurerm_storage_accountStorage accounts have no equivalent; object storage is tenant-scoped, so containers become buckets directly and the account is removed.
azurerm_storage_blobIndividual storage blobs (objects) are out of scope for the converter; upload objects with the S3 API or fm CLI after the bucket is created.
azurerm_subnet_nat_gateway_associationFrostmoln's outbound path is attached to the VPC, not to individual subnets: there is no per-subnet association, and the VPC has no outbound path at all until you add a frostmoln_gateway. It may have at most ONE, so every NAT gateway serving its subnets collapses into that one.
azurerm_subnet_network_security_group_associationSubnet-to-security-group associations have no equivalent; set security_groups on each converted frostmoln_instance in the subnet to the converted frostmoln_security_group id.
data.azurerm_subscriptionFrostmoln has no subscription layer and no data source for the one you are in; the nearest boundary is the tenant, which is provider configuration (tenant_id, or FROSTMOLN_TENANT_ID).
azurerm_user_assigned_identityThere is no standalone identity resource, but the pattern has a counterpart: a Kubernetes workload gets its identity from frostmoln_workload_identity_binding, and permissions from a frostmoln_iam_policy.
azurerm_virtual_network_gatewayVPN / ExpressRoute gateways have no equivalent; there is no managed site-to-site VPN or private interconnect service.
azurerm_virtual_network_peeringVirtual network peering has no equivalent; there is no cross-VPC peering primitive.

azurerm_application_insights ​

Instrument your application with an APM tool of your choice running on frostmoln_instance or a frostmoln_kubernetes_cluster.

data.azurerm_client_config ​

This data source reads nothing and creates nothing - it exposes who Terraform is authenticated as. The frostmoln provider already knows: it operates in exactly ONE tenant, taken from the provider's tenant_id (or FROSTMOLN_TENANT_ID), defaulting to the account's default tenant when neither is set. Use a variable for the tenant id and pass it to the provider, then reference that variable wherever data.azurerm_client_config.current.tenant_id was used - most commonly to scope a Key Vault or a role assignment, both of which are tenant-scoped on Frostmoln without being named. There is no counterpart at all for subscription_id (Frostmoln has no subscription layer), client_id, or object_id: the identity behind an API key is not addressable in configuration, and permissions are granted to the api_key, workload_identity, or group directly with frostmoln_iam_policy_attachment rather than to a principal id you look up first.

azurerm_container_app ​

Run containers on a frostmoln_kubernetes_cluster, which provides equivalent scheduling and scaling.

azurerm_container_registry ​

Add a frostmoln_container_registry to enable the tenant's registry — it takes no arguments, and the endpoint and namespace it exports are platform-assigned. Mint push/pull credentials with frostmoln_container_registry_credential, and use frostmoln_container_registry_cache where this registry was a pull-through cache of an upstream. A tenant has exactly one registry and one namespace, so several Azure registries collapse into one — tell them apart by repository name, and re-point every image reference at the new endpoint. SKU/tier, geo-replication, the admin account, retention policies and network rules have no counterpart; the platform manages storage, availability and access.

azurerm_cosmosdb_account ​

Re-model the data on a managed relational offer (frostmoln_postgres_instance / frostmoln_mysql_instance) or cache (frostmoln_redis_instance), or run a NoSQL engine yourself on frostmoln_instance.

azurerm_eventgrid_topic ​

Re-design event routing around the managed message broker or application-level webhooks.

azurerm_eventhub_namespace ​

Run a streaming platform yourself on frostmoln_instance, or re-design around the managed message broker where queue semantics suffice.

azurerm_federated_identity_credential ​

frostmoln_workload_identity_binding is the counterpart, and it is a resource you can write today. Write one per federated credential: cluster_id (the frostmoln_kubernetes_cluster the workload runs in), namespace and service_account (the two halves of the source subject, which is system:serviceaccount:<namespace>:<service-account>), and a grant. Prefer granting it with a frostmoln_iam_policy attached by frostmoln_iam_policy_attachment: that is the shape closest to the azurerm_role_assignment you are replacing, because a policy can name individual resources and deny explicitly. The alternative is the flat scopes list (concrete scopes such as compute:read, wildcards rejected), which grants across a whole service and cannot express a narrow rule. scopes is OPTIONAL - OMIT the attribute for a policy-granted binding (an empty list is not a valid spelling and is rejected). Three things to know about that path: a binding created before its policy is attached is INERT, and the token exchange refuses it rather than minting a credential that grants nothing; while a binding carries BOTH scopes and a policy the scopes are what apply, so the policy's additional authority takes effect only once the scopes are dropped - and dropping them NARROWS the workload to whatever the policy allows, which is never checked against what the scopes covered, so enumerate the binding's access before you drop them; and removing a binding's LAST grant is rejected, so delete the binding to remove a workload's access - which is also why terraform destroy has to take the binding first (-target), since destroying its only attachment is refused. One more step has no counterpart in this block: only a Kubernetes ServiceAccount annotated frostmoln.cloud/workload-identity=true gets a credential injected, so annotate the ServiceAccount as well as writing the binding. Before writing any of them, sort your federated credentials by WHAT the identity is for - several classes should not become a binding at all. An identity that exists only to run a cluster ADD-ON the platform installs itself becomes an entry in frostmoln_kubernetes_cluster.addons rather than a binding (read the offered keys from the frostmoln_kubernetes_addons data source; external-secrets and external-dns are there today). Three things about that: the set is applied ONCE at cluster creation, so changing it REPLACES the cluster; an explicit set replaces the platform defaults, so list external-secrets alongside anything you add if you still want it; and the addon replaces the DEPLOYMENT, not the credential - external-dns still needs the DNS feature on your tenant and a frostmoln_api_key delivered as a Kubernetes Secret before its webhook container starts, and external-secrets likewise authenticates to Frostmoln Secrets with an API key of its own. An identity that fed Azure Communication Services has no counterpart, because that product has none on Frostmoln. An identity a managed DATABASE trusted for Entra/AD authentication has no counterpart either: Frostmoln managed databases authenticate with a username and password - though a pod can avoid baking those in by FETCHING them, which a binding granted database:read allows. An identity used to read SECRETS becomes a binding whose policy allows the operation secrets:secrets:read; note that a three-segment operation string belongs in a POLICY and is not a valid flat scope (the coarse flat spelling is secrets:read), and putting one in scopes is accepted at create and then refused on every request. An ordinary application pod calling the Frostmoln API becomes a binding plus the policy its azurerm_role_assignment described. And one class looks like a binding but is not: an observability pod (loki, tempo, mimir) writing to blob storage speaks S3, and Frostmoln object bytes go directly to the object store rather than through the platform API, so it needs a frostmoln_s3_credential - scoped with allowed_buckets and allowed_actions, which unset grant every bucket and every action in the tenant, wider than the single storage account you are replacing. Its secret is returned only once, at create, so decide how it reaches the cluster before you write it. An application that calls the Frostmoln API itself can instead hold a binding and mint scoped S3 credentials at runtime; loki, tempo and mimir cannot, which is what makes them the exception rather than the rule. The converter does not emit a binding because nothing in this block supplies those values honestly: subject is almost always built from interpolated module outputs rather than a literal string, issuer is the AKS cluster's OIDC issuer URL and does not identify a Frostmoln cluster, and the PERMISSIONS are not here at all - in Azure they live in the separate azurerm_role_assignment resources that target the identity. A generated binding would therefore be placeholders in every position, which is a blocked block wearing a costume. Two further differences to plan for: there is no identity object to federate WITH (azurerm_user_assigned_identity has no counterpart - the binding is the identity), and the audience is fixed by the platform, so api://AzureADTokenExchange carries nothing over. The cluster needs the workload-identity add-on in its addons (at create or added later) for a pod to receive the credential. Federation covers Frostmoln-managed clusters only - a workload running anywhere else needs a frostmoln_api_key held as a secret.

azurerm_firewall ​

Express allow rules with frostmoln_security_group and frostmoln_security_group_rule; run a firewall appliance on frostmoln_instance for advanced L7 filtering.

Rule expressiveness is not the whole loss, though, and the other half is easier to miss. In the hub-and-spoke design this resource usually sits in, the firewall IS the spokes' outbound path — every one of them default-routes to it — so deleting it takes their internet with it. Frostmoln does not attach an outbound path to a VPC for you: a VPC without a frostmoln_gateway is an isolated network — no outbound internet, and, because the platform DNS resolver and the managed-service control plane are reached over routes that exist only while that path does, no name resolution and no reach to that control plane either. Add one to the converted VPC:

resource "frostmoln_gateway" "main" {
  vpc_id = frostmoln_vpc.main.id
  mode   = "public_ip"
}

The gateway's source address is drawn by the platform, and you do not choose which one you get, so it is not an address to publish. Where a partner allow-lists the address your traffic leaves from, allocate it yourself and name it on the gateway:

resource "frostmoln_public_ip" "egress" {}

resource "frostmoln_gateway" "main" {
  vpc_id       = frostmoln_vpc.main.id
  mode         = "public_ip"
  public_ip_id = frostmoln_public_ip.egress.id
}

That address is one of yours: listed among your Public IPs, drawn from the same public IPv4 quota your customer-facing addresses use, and billed as a Public IP. There is no shared-outbound-address option. See the azurerm_nat_gateway entry for the rest of it, including which provider version carries the resource.

The two halves are also scoped differently. One firewall in a hub served every spoke; a Frostmoln gateway belongs to ONE VPC and a VPC may have at most one, so a hub-and-spoke topology becomes one gateway per VPC, not one shared instance.

azurerm_key_vault ​

Frostmoln secrets are not grouped in a vault — access is granted from the principal side (a frostmoln_iam_policy attached with frostmoln_iam_policy_attachment) rather than by a vault access policy. The converter derives a commented policy candidate from the STANDALONE azurerm_key_vault_access_policy resource only; an access_policy block written inline here is removed with the vault, so check this resource for one before assuming nothing granted access. Keys and certificates stored in a Key Vault have no equivalent.

azurerm_key_vault_access_policy ​

There is no vault object to grant on - frostmoln_secret is tenant-scoped, so a secret has no per-vault access list to add a principal to. secret_permissions IS mapped, one Frostmoln operation per permission rather than a blanket wildcard: Get becomes secrets:secrets:read, List becomes secrets:secrets:list, Set becomes secrets:secrets:create plus secrets:secrets:update, Delete becomes secrets:secrets:delete, and Recover and Restore become secrets:secrets:create. ONE widening remains, and it belongs to the platform rather than to the translation, because one Frostmoln operation serves two routes Key Vault kept apart: the recover route is a POST authorized by HTTP method as secrets:secrets:create, and that same operation also authorizes creating NEW secrets under any name, so a policy granting Recover or Restore WITHOUT Set converts BROADER than it was in Key Vault. A second widening used to sit beside it and is now CLOSED: the lookup-by-name route returns the secret VALUE and selects it by query parameter rather than by path id, so it was once authorized by secrets:secrets:list - the same operation as the metadata listing - and List WITHOUT Get converted broader. Since secrets v0.11.5 that route takes an explicit secrets:secrets:read on the secret id it resolves, so :list is metadata-only and List WITHOUT Get converts FAITHFULLY at the route level - subject to the scope caveat below, which is what actually decides it for an API key. The candidate states the remaining widening where it applies, and stays silent where the source already conferred it. READ THAT CAVEAT BEFORE TREATING ANY PERMISSION THIS MAPPING LEAVES OUT AS WITHHELD. An API key must hold a flat legacy scope to reach these routes at all - secrets:read for Get and List, secrets:write for Set, Delete, Recover and Restore - and Frostmoln synthesizes each of those scopes into an allow that is UNIONED with your policy: secrets:read becomes an allow for the read AND list operations on every secrets resource, and secrets:write becomes an allow for create, update AND delete on every secrets resource. A union does not subtract. So an API key holding secrets:read is conferred Get AND List whichever of the two the policy grants, and one holding secrets:write is conferred Set, Delete, Recover and Restore alike - which subsumes the recover widening above rather than adding to it. Only an explicit deny rule withholds anything: a deny overrides every allow including the synthesized one, and the candidate cannot write it for you. Two secret permissions have no counterpart at all and are excluded rather than folded into a wildcard: Purge (a deleted secret is kept for its own recovery window and removed by a background job when that expires, so there is no purge operation to grant) and Backup (there is no secret export, and the nearest grant returns the plaintext, which is more than Backup conferred). key_permissions and certificate_permissions are NOT converted: there are no key or certificate objects to grant on - Frostmoln stores secrets, and a TLS certificate is simply a secret referenced by a load-balancer listener (frostmoln_lb_listener.tls_certificate_id). Nothing is created automatically and nothing counts as converted: a block of THIS type stays blocked, so terraform validate still fails until you act on it. That does not hold for the inline access_policy form, which is removed with its vault at info severity - see azurerm_key_vault. tenant_id and object_id have no counterpart either: the principal is the frostmoln_api_key, workload identity, or group you attach to, named directly rather than looked up by object id, and the converter cannot choose it for you.

azurerm_linux_function_app ​

Package the function as a long-running service on frostmoln_instance or a frostmoln_kubernetes_cluster.

azurerm_linux_web_app ​

For a static or PHP site, create a frostmoln_apache_instance or a frostmoln_nginx_instance, bind the hostname with a frostmoln_webserver_domain (which carries its TLS), and ship the content as a frostmoln_webserver_deployment archive rather than through a deployment slot or a container image. An App Service running .NET, Node, Python, Java or Ruby has NO counterpart on that offer: run it on a frostmoln_instance or a frostmoln_kubernetes_cluster and expose it with a frostmoln_application_gateway. Deployment slots, autoscale rules, always-on and the app-settings/connection-string model have no equivalent either — settings travel with the deployed content, and a frostmoln_secret holds what used to be a connection string.

azurerm_log_analytics_workspace ​

Use the platform's built-in metrics and logs, or run your own log store on frostmoln_instance.

azurerm_logic_app_workflow ​

Run a workflow engine yourself on frostmoln_instance or a frostmoln_kubernetes_cluster.

azurerm_mysql_flexible_database ​

This is a real gap, not a missing mapping: no Frostmoln API and no provider resource creates a database inside a managed instance, so nothing here converts. Read the MySQL case carefully - the credentials endpoint returns a connection pointed at the mysql SYSTEM schema (users and grants), which is not a place to put application data. Create your database the way you would on any MySQL server: CREATE DATABASE over that connection, which stays declarative if you point the community mysql Terraform provider at the instance. charset and collation carry over as arguments to that statement rather than as platform settings. Two practical notes. The connection is reachable from inside the VPC via the instance's private_ip; from a laptop or CI runner outside it, use fm forward <instance-id> <local-port> and point the provider at the local end. And the provider exports admin_username but NOT the password - fetch it with fm database mysql instance credentials <id> (or GET /api/v1/tenants/{tenant}/databases/{id}/credentials). One Azure server holding several databases can stay one instance; nothing in the frostmoln provider will manage those databases for you.

azurerm_mysql_flexible_server_active_directory_administrator ​

This is a real gap, not a missing mapping. Azure AD authentication lets a directory principal log in to the server as itself; Frostmoln has no equivalent - a managed instance authenticates with database credentials only, and platform identity (users, api keys, workload identities) does not reach inside the database. The converted instance exports admin_username; the platform-issued password is NOT exported by the provider, so fetch it with fm database mysql instance credentials <id> (or GET /api/v1/tenants/{tenant}/databases/{id}/credentials) and create the users your application needs with SQL over that connection, as on any self-managed MySQL. Plan for the operational difference: sign-ins to the database are not covered by your identity provider, so per-person access is database users plus credential rotation rather than directory membership, and there is no group-to-role mapping to carry over.

azurerm_mysql_flexible_server_configuration ​

Be careful with this one: the shape of the answer exists and the effect does not. Frostmoln has a parameter-group API (POST/GET/PUT/DELETE on /api/v1/tenants/{tenant}/database-parameter-groups) and frostmoln_mysql_instance takes an optional parameter_group_id, so a group can be created and referenced today - but the provisioning path never reads it, and the apply endpoint (POST /databases/{id}/parameters/apply) accepts the call and does nothing. Nothing you put in a group reaches the server. Three further limits, so you are not misled by a successful API call: parameter NAMES are never validated, so an unknown or platform-managed setting is stored and silently ignored rather than rejected; neither fm nor the portal exposes parameter groups, so the API is the only way to create one (fm only has a --parameter-group flag that REFERENCES an existing id at instance create); and there is no report that tells you which settings took effect. The practical migration path: record the settings from the blocks commented out here, size the instance so the platform defaults are adequate, and re-tune once parameter apply ships. Where a setting is load-bearing for your workload, raise it before you migrate.

azurerm_mysql_flexible_server_firewall_rule ​

A managed database instance is private and stays private - the database service has no expose action, so it is never published to the internet and there is nothing for these rules to narrow. Reachability comes from placement: set the converted instance's vpc_id and subnet_id, and the platform opens the engine port to that VPC for you. (Other managed offers do support an opt-in expose; databases do not.) The instance's security group is created and owned by the platform and is not an argument on frostmoln_mysql_instance, so there is nothing to reference from your configuration - and nothing worth adding, since the engine port is already open VPC-wide and no rule can make the instance reachable from outside the VPC. Two Azure idioms need a decision rather than a translation. A rule with start_ip_address and end_ip_address both 0.0.0.0 is the 'Allow public access from any Azure service' switch; it has no counterpart and nothing replaces it - run the client inside the VPC. A rule opening the server to an office or CI range means that client sits outside the VPC today: for a laptop or a CI runner use fm forward <instance-id> <local-port>, which brokers a session to the engine port with no public IP, no open port and no VPN (sessions end after 10 minutes idle or 1 hour); for a permanent workload, move it into the VPC. Ranges do not carry over in shape either - Azure takes an arbitrary first-to-last IP range, while Frostmoln security groups take CIDRs, and an arbitrary range is not always one CIDR.

azurerm_nat_gateway ​

This is not a resource the platform makes unnecessary — it is one you have to replace. A Frostmoln VPC's outbound path is an explicit resource, and a VPC without one is an isolated network: no outbound internet, and — because the platform DNS resolver and the managed-service control plane are reached over routes that exist only while that path does — no name resolution and no reach to that control plane either. Attach one to the converted VPC:

resource "frostmoln_gateway" "main" {
  vpc_id = frostmoln_vpc.main.id
  mode   = "public_ip"
}

That gateway IS the outbound translation this NAT gateway performed: every instance in the VPC egresses through it. Its source address is drawn by the platform, and you do not choose which one you get, so it is not an address to publish. Where a remote peer allow-lists your source address — which is what the public IP (or prefix) you associated with the NAT gateway gave you — allocate the address yourself and name it:

resource "frostmoln_public_ip" "egress" {}

resource "frostmoln_gateway" "main" {
  vpc_id       = frostmoln_vpc.main.id
  mode         = "public_ip"
  public_ip_id = frostmoln_public_ip.egress.id
}

That address is one of yours: listed among your Public IPs, drawn from the same public IPv4 quota your customer-facing addresses use, and billed as a Public IP. There is no shared-outbound-address option — a VPC egresses through its own gateway, never through an address held in common with other tenants.

The converter does not write the gateway for you, deliberately: connectivity is a stated choice, so that a VPC cannot acquire an outbound path — or a billable public address — because a field was omitted.

Two shapes do not carry over at all. A VPC has AT MOST ONE gateway, while this configuration may hold several NAT gateways (typically one per zone or per subnet group) for what becomes a single VPC; one frostmoln_gateway replaces all of them, and that many-to-one collapse is left to you to make rather than performed quietly. And the gateway is attached to the VPC as a whole — there is no per-subnet association, so a design that gave some subnets a NAT gateway and left others without one has no equivalent.

The same gateway can be created outside Terraform with fm network gateway create.

It adopts rather than duplicates. If the VPC already has a gateway — one the platform attached implicitly because a public IP was associated with an instance in it, or one created outside Terraform later — import that one rather than creating a second; the platform refuses the second with GATEWAY_EXISTS. The resource is one-to-one with its VPC, so the VPC's own id is the import id:

terraform import frostmoln_gateway.main <vpc-id>

azurerm_nat_gateway_public_ip_association ​

This resource nominates the public IP a NAT gateway uses as its outbound source address. Frostmoln has no separate association: the VPC's gateway exposes one source address, and it is named on the gateway. Left unnamed, the gateway egresses from an address the platform draws — one you do not choose and should not publish. Naming a Public IP you own is the analogue of this association, and the answer when a remote peer allow-lists your source IP:

resource "frostmoln_public_ip" "egress" {}

resource "frostmoln_gateway" "main" {
  vpc_id       = frostmoln_vpc.main.id
  mode         = "public_ip"
  public_ip_id = frostmoln_public_ip.egress.id
}

That address is one of yours: listed among your Public IPs, drawn from the same public IPv4 quota your customer-facing addresses use, and billed as a Public IP. You still cannot ask for a PARTICULAR address — the platform allocates it — so an allow-list entry has to be updated once to the address you are given.

The gateway itself is not created for you; see azurerm_nat_gateway, and add it with or without the public_ip_id above:

resource "frostmoln_gateway" "main" {
  vpc_id = frostmoln_vpc.main.id
  mode   = "public_ip"
}

The same gateway can be created outside Terraform with fm network gateway create.

It adopts rather than duplicates. If the VPC already has a gateway — one the platform attached implicitly because a public IP was associated with an instance in it, or one created outside Terraform later — import that one rather than creating a second; the platform refuses the second with GATEWAY_EXISTS. The resource is one-to-one with its VPC, so the VPC's own id is the import id:

terraform import frostmoln_gateway.main <vpc-id>

azurerm_nat_gateway_public_ip_prefix_association ​

A public IP prefix gives an Azure NAT gateway a contiguous block of source addresses, usually for SNAT port headroom. Frostmoln has neither half of that: there is no public IP prefix resource, and a VPC's gateway carries exactly ONE source address.

Left to itself the gateway egresses from an address the platform draws, which you do not choose and should not publish. The closest thing to a prefix you allocated for allow-listing is a single Public IP of your own, named on the gateway:

resource "frostmoln_public_ip" "egress" {}

resource "frostmoln_gateway" "main" {
  vpc_id       = frostmoln_vpc.main.id
  mode         = "public_ip"
  public_ip_id = frostmoln_public_ip.egress.id
}

That address is one of yours: listed among your Public IPs, drawn from the same public IPv4 quota your customer-facing addresses use, and billed as a Public IP. It is one address, not a range, and not one you can nominate.

If a partner allow-lists a whole prefix today, that expectation has to be renegotiated to a single address before this configuration will work.

The gateway itself is not created for you; see azurerm_nat_gateway, and add it with or without the public_ip_id above:

resource "frostmoln_gateway" "main" {
  vpc_id = frostmoln_vpc.main.id
  mode   = "public_ip"
}

The same gateway can be created outside Terraform with fm network gateway create.

It adopts rather than duplicates. If the VPC already has a gateway — one the platform attached implicitly because a public IP was associated with an instance in it, or one created outside Terraform later — import that one rather than creating a second; the platform refuses the second with GATEWAY_EXISTS. The resource is one-to-one with its VPC, so the VPC's own id is the import id:

terraform import frostmoln_gateway.main <vpc-id>

azurerm_network_interface_security_group_association ​

Azure attaches a network security group to the interface, but frostmoln_instance carries security_groups directly. This association is left for you to wire explicitly: on the platform's default-drop model an instance left without its security group is unreachable (unsecured by omission / no ingress), not wide-open — so restore the intended access by setting security_groups = [frostmoln_security_group.<n>.id] on the instance.

azurerm_postgresql_flexible_server_active_directory_administrator ​

This is a real gap, not a missing mapping. Azure AD authentication lets a directory principal log in to the server as itself; Frostmoln has no equivalent - a managed instance authenticates with database credentials only, and platform identity (users, api keys, workload identities) does not reach inside the database. The converted instance exports admin_username; the platform-issued password is NOT exported by the provider, so fetch it with fm database postgres instance credentials <id> (or GET /api/v1/tenants/{tenant}/databases/{id}/credentials) and create the roles your application needs with SQL over that connection, as on any self-managed PostgreSQL. Plan for the operational difference: sign-ins to the database are not covered by your identity provider, so per-person access is database roles plus credential rotation rather than directory membership, and there is no group-to-role mapping to carry over.

azurerm_postgresql_flexible_server_configuration ​

Be careful with this one: the shape of the answer exists and the effect does not. Frostmoln has a parameter-group API (POST/GET/PUT/DELETE on /api/v1/tenants/{tenant}/database-parameter-groups) and frostmoln_postgres_instance takes an optional parameter_group_id, so a group can be created and referenced today - but the provisioning path never reads it, and the apply endpoint (POST /databases/{id}/parameters/apply) accepts the call and does nothing. Nothing you put in a group reaches the server. Three further limits, so you are not misled by a successful API call: parameter NAMES are never validated, so an unknown or platform-managed setting is stored and silently ignored rather than rejected; neither fm nor the portal exposes parameter groups, so the API is the only way to create one (fm only has a --parameter-group flag that REFERENCES an existing id at instance create); and there is no report that tells you which settings took effect. The practical migration path: record the settings from the blocks commented out here, size the instance so the platform defaults are adequate, and re-tune once parameter apply ships. Where a setting is load-bearing for your workload, raise it before you migrate.

azurerm_postgresql_flexible_server_database ​

This is a real gap, not a missing mapping: no Frostmoln API and no provider resource creates a database inside a managed instance, so nothing here converts. What you do get is usable - the instance is provisioned with the stock postgres database AND a database named after the admin user (pgadmin), owned by it, which is a fine landing spot for a single-database migration. For the rest, do what you would on any PostgreSQL server: CREATE DATABASE over the connection, which stays declarative if you point the community postgresql Terraform provider at the instance. collation and charset carry over as arguments to that statement rather than as platform settings. Two practical notes. The connection is reachable from inside the VPC via the instance's private_ip; from a laptop or CI runner outside it, use fm forward <instance-id> <local-port> and point the provider at the local end. And the provider exports admin_username but NOT the password - fetch it with fm database postgres instance credentials <id> (or GET /api/v1/tenants/{tenant}/databases/{id}/credentials). One Azure server holding several databases can stay one instance; nothing in the frostmoln provider will manage those databases for you.

azurerm_postgresql_flexible_server_firewall_rule ​

A managed database instance is private and stays private - the database service has no expose action, so it is never published to the internet and there is nothing for these rules to narrow. Reachability comes from placement: set the converted instance's vpc_id and subnet_id, and the platform opens the engine port to that VPC for you. (Other managed offers do support an opt-in expose; databases do not.) The instance's security group is created and owned by the platform and is not an argument on frostmoln_postgres_instance, so there is nothing to reference from your configuration - and nothing worth adding, since the engine port is already open VPC-wide and no rule can make the instance reachable from outside the VPC. Two Azure idioms need a decision rather than a translation. A rule with start_ip_address and end_ip_address both 0.0.0.0 is the 'Allow public access from any Azure service' switch; it has no counterpart and nothing replaces it - run the client inside the VPC. A rule opening the server to an office or CI range means that client sits outside the VPC today: for a laptop or a CI runner use fm forward <instance-id> <local-port>, which brokers a session to the engine port with no public IP, no open port and no VPN (sessions end after 10 minutes idle or 1 hour); for a permanent workload, move it into the VPC. Ranges do not carry over in shape either - Azure takes an arbitrary first-to-last IP range, while Frostmoln security groups take CIDRs, and an arbitrary range is not always one CIDR.

azurerm_private_dns_zone ​

This is a real gap, not a missing mapping. Every Frostmoln zone is public - it is delegated to the nameserver set the platform assigns it (read it back from name_servers; it is per zone, not one pair shared by the platform) and answers the same records to everyone, and there is no VPC-scoped view of a zone. You cannot approximate one either: A and AAAA records holding a private, loopback, link-local or unspecified address are rejected, so a public zone cannot be loaded with 10.x names as a substitute. The good news is that the usual reason for an Azure private zone rarely survives the move. Private zones are most often there to give a managed service a stable in-VPC name, and Frostmoln managed services do not need one - a frostmoln_postgres_instance, frostmoln_mysql_instance, frostmoln_redis_instance or frostmoln_valkey_instance is created directly in your VPC and subnet (vpc_id, subnet_id) and exports private_ip, so Terraform can wire a consumer to the instance by referencing that attribute rather than by resolving a name. That reference is also more accurate than the Azure original, because it breaks the plan if the instance goes away. If you genuinely need names rather than addresses inside the VPC - service discovery between your own VMs, or an application that only takes a hostname - run a resolver on an instance in the VPC and point the subnet at it with frostmoln_subnet.dns_servers, which is a list of resolvers handed to every instance on that subnet by DHCP. Two things to know about it: the list REPLACES the platform resolvers on that subnet rather than adding to them, so point it at a forwarding resolver unless you intend to lose public name resolution there; and set it when you create the subnet, because changing it later replaces the subnet. Public names for anything internet-facing stay in a frostmoln_dns_zone as normal. soa_record is platform-managed on every zone, and tags carry no meaning without a zone to hold them.

This resource only exists to give a private zone its scope, so it disappears with the zone rather than being unsupported on its own terms. A Frostmoln zone is public and is not attached to a VPC at all: there is no per-VPC view to link, and no registration_enabled equivalent - instances are never auto-registered into a zone. Read azurerm_private_dns_zone for what to do instead; briefly, managed services are reachable in the VPC by their exported private_ip, and if you need in-VPC hostnames, run your own resolver and publish it with frostmoln_subnet.dns_servers. Watch for a converted resource that depended on this link: an Azure managed database is commonly wired to its private zone through private_dns_zone_id, and on the platform that same instance gets its VPC placement from vpc_id and subnet_id instead, so the dependency is satisfied by placement rather than by DNS.

azurerm_private_endpoint ​

Place managed services (databases, caches) in your VPC subnet; there is no separate private-link resource to create.

azurerm_resource_group ​

Frostmoln has no resource-group container — every resource belongs to the tenant. The group's name is not propagated to child resources automatically; if you relied on it for organization, add it as a tag on the converted resources yourself.

azurerm_role_assignment ​

The engine is live and enforcing across the customer-facing services, so this is a conversion gap rather than a platform gap - but three things stop the converter from finishing the job, and each of them is a decision only you can make. First, THE PRINCIPAL: Frostmoln has no standalone machine-identity object to create and grant to, so the attachment must name an api_key, a workload identity (managed Kubernetes only), or a group that already exists. Second, THE SCOPE WIDENS: an Azure assignment is scoped to a subscription, a resource group or a single resource, and Frostmoln has neither a subscription nor a resource-group layer, while a target FRN's region segment must be * - so the nearest expressible grant is tenant-wide and all-region, which is broader than what you had. Third, THE VOCABULARY DIFFERS: an Azure built-in role is a set of ARM control-plane and data-plane actions, a Frostmoln policy rule is a set of service:resource:action operations over FRN targets, and no mechanical translation between them is exact. For a curated set of common Azure built-in roles the converter writes a COMMENTED candidate policy into a generated frostmoln-iam-candidates.tf, stating the mapped operations and how faithful the mapping is; review it, narrow it, choose the principal, then uncomment it to adopt it. Authoring runs under an fm CLI or portal login session - a machine token may not author or attach a policy - and your own authority must already cover every operation the policy grants or the create is refused.

azurerm_servicebus_namespace ​

For classic queue/exchange messaging, run a managed broker (see the messaging offer). Topic/subscription fan-out, sessions, and dead-letter queues must be re-designed on the broker's own primitives.

azurerm_storage_account ​

Frostmoln has no storage-account container — each azurerm_storage_container becomes a frostmoln_bucket on its own. If you relied on the account name for global bucket uniqueness, fold it into the bucket names. S3-compatible credentials replace the account access keys (create a frostmoln_s3_credential).

azurerm_storage_blob ​

Object contents are not managed as Terraform resources on Frostmoln — convert the containing azurerm_storage_container to a frostmoln_bucket and upload objects separately.

azurerm_subnet_nat_gateway_association ​

This resource is what actually gives an Azure subnet outbound connectivity, so losing it is not a formality. Frostmoln models the outbound path one level up: a gateway belongs to the VPC and serves every subnet in it. There is no per-subnet association, so a design that deliberately gave some subnets a NAT gateway and left others with none has no equivalent — isolate those workloads with security groups, or put them in a separate VPC with no gateway.

The VPC-wide path is not implied either. A Frostmoln VPC without a gateway is an isolated network: no outbound internet, and — because the platform DNS resolver and the managed-service control plane are reached over routes that exist only while that path does — no name resolution and no reach to that control plane either. Attach one to the converted VPC:

resource "frostmoln_gateway" "main" {
  vpc_id = frostmoln_vpc.main.id
  mode   = "public_ip"
}

The gateway's source address is drawn by the platform, and you do not choose which one you get, so it is not an address to publish. Where a partner allow-lists your source address, or DNS points at it, allocate the address yourself and name it — the gateway then egresses from that one:

resource "frostmoln_public_ip" "egress" {}

resource "frostmoln_gateway" "main" {
  vpc_id       = frostmoln_vpc.main.id
  mode         = "public_ip"
  public_ip_id = frostmoln_public_ip.egress.id
}

That address is one of yours: listed among your Public IPs, drawn from the same public IPv4 quota your customer-facing addresses use, and billed as a Public IP. There is no shared-outbound-address option — a VPC egresses through its own gateway, never through an address held in common with other tenants. The converter writes no gateway: connectivity is a stated choice, so that a VPC cannot acquire an outbound path — or a billable public address — because a field was omitted.

The same gateway can be created outside Terraform with fm network gateway create.

It adopts rather than duplicates. If the VPC already has a gateway — one the platform attached implicitly because a public IP was associated with an instance in it, or one created outside Terraform later — import that one rather than creating a second; the platform refuses the second with GATEWAY_EXISTS. The resource is one-to-one with its VPC, so the VPC's own id is the import id:

terraform import frostmoln_gateway.main <vpc-id>

azurerm_subnet_network_security_group_association ​

Azure applies a network security group to every interface in a subnet; frostmoln_instance carries security_groups per instance. Apply the converted security group to each instance explicitly: on the platform's default-drop model an instance left without its rules is unreachable (unsecured by omission / no ingress), not wide-open — wiring it restores the intended access.

data.azurerm_subscription ​

An Azure subscription is a billing and isolation container holding resource groups. Frostmoln has two levels, not three: the organization is the customer, and a tenant is an isolation boundary inside it - there is nothing between them and no resource-group layer below. The provider operates in exactly ONE tenant (tenant_id, or FROSTMOLN_TENANT_ID, defaulting to the account's default tenant); to span tenants, declare a second provider with an alias. Nothing looks the current tenant up at plan time, so replace data.azurerm_subscription.current.subscription_id and .tenant_id with the variable you pass to the provider. display_name has no equivalent that configuration can read.

azurerm_user_assigned_identity ​

The AKS pattern - a user-assigned identity federated to a service account, then granted access by role assignments - maps to frostmoln_workload_identity_binding (it binds a namespace and service account in a Frostmoln-managed Kubernetes cluster, exchanging the projected ServiceAccount token for a short-lived platform token) granted by a frostmoln_iam_policy attached to that workload_identity. The policy is the counterpart of the role assignments and the preferred grant; the binding's own flat scopes list is OPTIONAL and coarser. There is no identity OBJECT to create first, so this resource disappears rather than converting. Not every identity earns a binding, though, so sort them by purpose first: one that exists only for a cluster add-on the platform installs itself becomes an entry in frostmoln_kubernetes_cluster.addons, chosen at cluster create and still needing its own API-key Secret; one that only fed Azure Communication Services has no counterpart; one a managed database trusted for Entra/AD authentication has none either, since Frostmoln databases authenticate with a username and password a binding can fetch but not replace; and an observability pod writing to blob storage needs a frostmoln_s3_credential rather than a binding, because it speaks S3. The worked breakdown is under azurerm_federated_identity_credential. Outside a Frostmoln-managed cluster there is no federation: a workload elsewhere needs a frostmoln_api_key held as a secret.

azurerm_virtual_network_gateway ​

Terminate VPNs on a self-managed instance (frostmoln_instance) inside your VPC if you need hybrid connectivity.

azurerm_virtual_network_peering ​

Design connectivity within a single VPC, or route between services over their public IPs.


Trademarks. Frostmoln is an independent cloud provider and a competing alternative to Amazon Web Services, Microsoft Azure, Google Cloud, and Oracle Cloud. Frostmoln is not affiliated with, endorsed by, or sponsored by any of them. AWS, Azure, Google Cloud, and Oracle Cloud are trademarks of their respective owners; all references are nominative and for interoperability only.