diff --git a/.gitignore b/.gitignore
index fbbbba51..939d0957 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,4 +48,6 @@ terraform-provider-proxmox*
# VScode / Cursor
.vscode/settings.json
+.cursor/
.cursorrules
+coverage.out
diff --git a/docs/data-sources/virtual_environment_vm2.md b/docs/data-sources/virtual_environment_vm2.md
index 2a348629..57702630 100644
--- a/docs/data-sources/virtual_environment_vm2.md
+++ b/docs/data-sources/virtual_environment_vm2.md
@@ -28,6 +28,7 @@ This is an experimental implementation of a Proxmox VM datasource using Plugin F
- `cpu` (Attributes) The CPU configuration. (see [below for nested schema](#nestedatt--cpu))
- `description` (String) The description of the VM.
- `name` (String) The name of the VM.
+- `rng` (Attributes) The RNG (Random Number Generator) configuration. (see [below for nested schema](#nestedatt--rng))
- `tags` (Set of String) The tags assigned to the VM.
- `template` (Boolean) Whether the VM is a template.
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
@@ -62,6 +63,16 @@ Optional:
- `units` (Number) CPU weight for a VM
+
+### Nested Schema for `rng`
+
+Optional:
+
+- `max_bytes` (Number) Maximum bytes of entropy allowed to get injected into the guest every period.
+- `period` (Number) Period in milliseconds to limit entropy injection to the guest.
+- `source` (String) The entropy source for the RNG device.
+
+
### Nested Schema for `timeouts`
diff --git a/docs/resources/virtual_environment_vm.md b/docs/resources/virtual_environment_vm.md
index e36d6bf8..2c858d88 100755
--- a/docs/resources/virtual_environment_vm.md
+++ b/docs/resources/virtual_environment_vm.md
@@ -484,6 +484,10 @@ output "ubuntu_vm_public_key" {
- `pool_id` - (Optional) The identifier for a pool to assign the virtual machine to.
- `protection` - (Optional) Sets the protection flag of the VM. This will disable the remove VM and remove disk operations (defaults to `false`).
- `reboot` - (Optional) Reboot the VM after initial creation. (defaults to `false`)
+- `rng` - (Optional) The random number generator configuration. Can only be set by `root@pam.`
+ - `source` - The file on the host to gather entropy from. In most cases, `/dev/urandom` should be preferred over `/dev/random` to avoid entropy-starvation issues on the host.
+ - `max_bytes` - (Optional) Maximum bytes of entropy allowed to get injected into the guest every `period` milliseconds (defaults to `1024`). Prefer a lower value when using `/dev/random` as source.
+ - `period` - (Optional) Every `period` milliseconds the entropy-injection quota is reset, allowing the guest to retrieve another `max_bytes` of entropy (defaults to `1000`).
- `serial_device` - (Optional) A serial device (multiple blocks supported).
- `device` - (Optional) The device (defaults to `socket`).
- `/dev/*` - A host serial device.
diff --git a/docs/resources/virtual_environment_vm2.md b/docs/resources/virtual_environment_vm2.md
index a2656af9..d58c50ca 100644
--- a/docs/resources/virtual_environment_vm2.md
+++ b/docs/resources/virtual_environment_vm2.md
@@ -36,6 +36,7 @@ The attributes are also marked as optional to allow the practitioner to set (or
- `description` (String) The description of the VM.
- `id` (Number) The unique identifier of the VM in the Proxmox cluster.
- `name` (String) The name of the VM. Doesn't have to be unique.
+- `rng` (Attributes) Configure the RNG (Random Number Generator) device. The RNG device provides entropy to guests to ensure good quality random numbers for guest applications that require them. Can only be set by `root@pam.`See the [Proxmox documentation](https://pve.proxmox.com/pve-docs/pve-admin-guide.html#qm_virtual_machines_settings) for more information. (see [below for nested schema](#nestedatt--rng))
- `stop_on_destroy` (Boolean) Set to true to stop (rather than shutdown) the VM on destroy (defaults to `false`).
- `tags` (Set of String) The tags assigned to the VM.
- `template` (Boolean) Set to true to create a VM template.
@@ -79,6 +80,16 @@ Optional:
- `units` (Number) CPU weight for a VM. Argument is used in the kernel fair scheduler. The larger the number is, the more CPU time this VM gets. Number is relative to weights of all the other running VMs.
+
+### Nested Schema for `rng`
+
+Optional:
+
+- `max_bytes` (Number) Maximum bytes of entropy allowed to get injected into the guest every period. Use 0 to disable limiting (potentially dangerous).
+- `period` (Number) Period in milliseconds to limit entropy injection to the guest. Use 0 to disable limiting (potentially dangerous).
+- `source` (String) The file on the host to gather entropy from. In most cases, `/dev/urandom` should be preferred over `/dev/random` to avoid entropy-starvation issues on the host.
+
+
### Nested Schema for `timeouts`
diff --git a/fwprovider/test/resource_vm_test.go b/fwprovider/test/resource_vm_test.go
index fae92eb5..cd397cbd 100644
--- a/fwprovider/test/resource_vm_test.go
+++ b/fwprovider/test/resource_vm_test.go
@@ -337,6 +337,64 @@ func TestAccResourceVM(t *testing.T) {
),
},
}},
+ {"update rng block", []resource.TestStep{
+ {
+ Config: te.RenderConfig(`
+ resource "proxmox_virtual_environment_vm" "test_vm" {
+ node_name = "{{.NodeName}}"
+ started = false
+
+ rng {
+ source = "/dev/urandom"
+ }
+ }`, WithRootUser()),
+ Check: resource.ComposeTestCheckFunc(
+ ResourceAttributes("proxmox_virtual_environment_vm.test_vm", map[string]string{
+ "rng.0.source": "/dev/urandom",
+ "rng.0.max_bytes": "1024",
+ "rng.0.period": "1000",
+ }),
+ ),
+ }, {
+ Config: te.RenderConfig(`
+ resource "proxmox_virtual_environment_vm" "test_vm" {
+ node_name = "{{.NodeName}}"
+ started = false
+
+ rng {
+ source = "/dev/urandom"
+ max_bytes = 2048
+ period = 500
+ }
+ }`, WithRootUser()),
+ Check: resource.ComposeTestCheckFunc(
+ ResourceAttributes("proxmox_virtual_environment_vm.test_vm", map[string]string{
+ "rng.0.source": "/dev/urandom",
+ "rng.0.max_bytes": "2048",
+ "rng.0.period": "500",
+ }),
+ ),
+ }, {
+ Config: te.RenderConfig(`
+ resource "proxmox_virtual_environment_vm" "test_vm" {
+ node_name = "{{.NodeName}}"
+ started = false
+
+ rng {
+ source = "/dev/random"
+ max_bytes = 512
+ period = 200
+ }
+ }`, WithRootUser()),
+ Check: resource.ComposeTestCheckFunc(
+ ResourceAttributes("proxmox_virtual_environment_vm.test_vm", map[string]string{
+ "rng.0.source": "/dev/random",
+ "rng.0.max_bytes": "512",
+ "rng.0.period": "200",
+ }),
+ ),
+ },
+ }},
}
for _, tt := range tests {
diff --git a/fwprovider/vm/datasource_schema.go b/fwprovider/vm/datasource_schema.go
index 3c650969..a44661da 100644
--- a/fwprovider/vm/datasource_schema.go
+++ b/fwprovider/vm/datasource_schema.go
@@ -1,3 +1,9 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
package vm
import (
@@ -9,6 +15,7 @@ import (
"github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cpu"
+ "github.com/bpg/terraform-provider-proxmox/fwprovider/vm/rng"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/vga"
)
@@ -53,6 +60,7 @@ func (d *Datasource) Schema(
Description: "The name of the node where the VM is provisioned.",
Required: true,
},
+ "rng": rng.DataSourceSchema(),
"tags": stringset.ResourceAttribute("The tags assigned to the VM.", ""),
"template": schema.BoolAttribute{
Description: "Whether the VM is a template.",
diff --git a/fwprovider/vm/model.go b/fwprovider/vm/model.go
index 3d3f78a1..1ac09bda 100644
--- a/fwprovider/vm/model.go
+++ b/fwprovider/vm/model.go
@@ -18,6 +18,7 @@ import (
"github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cdrom"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cpu"
+ "github.com/bpg/terraform-provider-proxmox/fwprovider/vm/rng"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/vga"
"github.com/bpg/terraform-provider-proxmox/proxmox"
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
@@ -38,6 +39,7 @@ type Model struct {
ID types.Int64 `tfsdk:"id"`
Name types.String `tfsdk:"name"`
NodeName types.String `tfsdk:"node_name"`
+ RNG rng.Value `tfsdk:"rng"`
StopOnDestroy types.Bool `tfsdk:"stop_on_destroy"`
Tags stringset.Value `tfsdk:"tags"`
Template types.Bool `tfsdk:"template"`
@@ -85,6 +87,7 @@ func read(ctx context.Context, client proxmox.Client, model *Model, diags *diag.
// Blocks
model.CPU = cpu.NewValue(ctx, config, diags)
+ model.RNG = rng.NewValue(ctx, config, diags)
model.VGA = vga.NewValue(ctx, config, diags)
model.CDROM = cdrom.NewValue(ctx, config, diags)
diff --git a/fwprovider/vm/resource.go b/fwprovider/vm/resource.go
index e2634dbc..9fd15f55 100644
--- a/fwprovider/vm/resource.go
+++ b/fwprovider/vm/resource.go
@@ -24,6 +24,7 @@ import (
"github.com/bpg/terraform-provider-proxmox/fwprovider/config"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cdrom"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cpu"
+ "github.com/bpg/terraform-provider-proxmox/fwprovider/vm/rng"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/vga"
"github.com/bpg/terraform-provider-proxmox/proxmox"
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
@@ -158,6 +159,7 @@ func (r *Resource) create(ctx context.Context, plan Model, diags *diag.Diagnosti
// fill out create body fields with values from other resource blocks
cdrom.FillCreateBody(ctx, plan.CDROM, createBody, diags)
cpu.FillCreateBody(ctx, plan.CPU, createBody, diags)
+ rng.FillCreateBody(ctx, plan.RNG, createBody, diags)
vga.FillCreateBody(ctx, plan.VGA, createBody, diags)
if diags.HasError() {
@@ -205,6 +207,8 @@ func (r *Resource) clone(ctx context.Context, plan Model, diags *diag.Diagnostic
Name: plan.Name,
Description: plan.Description,
NodeName: plan.NodeName,
+ RNG: plan.RNG,
+ VGA: plan.VGA,
}
read(ctx, r.client, &clone, diags)
@@ -336,6 +340,7 @@ func (r *Resource) update(ctx context.Context, plan, state Model, isClone bool,
// fill out update body fields with values from other resource blocks
cdrom.FillUpdateBody(ctx, plan.CDROM, state.CDROM, updateBody, isClone, diags)
cpu.FillUpdateBody(ctx, plan.CPU, state.CPU, updateBody, isClone, diags)
+ rng.FillUpdateBody(ctx, plan.RNG, state.RNG, updateBody, isClone, diags)
vga.FillUpdateBody(ctx, plan.VGA, state.VGA, updateBody, isClone, diags)
if !updateBody.IsEmpty() {
diff --git a/fwprovider/vm/resource_schema.go b/fwprovider/vm/resource_schema.go
index 37483656..5e3ebeb6 100644
--- a/fwprovider/vm/resource_schema.go
+++ b/fwprovider/vm/resource_schema.go
@@ -25,6 +25,7 @@ import (
"github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cdrom"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cpu"
+ "github.com/bpg/terraform-provider-proxmox/fwprovider/vm/rng"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/vga"
)
@@ -89,6 +90,7 @@ func (r *Resource) Schema(
Description: "The name of the node where the VM is provisioned.",
Required: true,
},
+ "rng": rng.ResourceSchema(),
"stop_on_destroy": schema.BoolAttribute{
Description: "Set to true to stop (rather than shutdown) the VM on destroy.",
MarkdownDescription: "Set to true to stop (rather than shutdown) the VM on destroy (defaults to `false`).",
diff --git a/fwprovider/vm/rng/datasource_schema.go b/fwprovider/vm/rng/datasource_schema.go
new file mode 100644
index 00000000..ed174f88
--- /dev/null
+++ b/fwprovider/vm/rng/datasource_schema.go
@@ -0,0 +1,41 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package rng
+
+import (
+ "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+)
+
+// DataSourceSchema defines the schema for the RNG datasource.
+func DataSourceSchema() schema.Attribute {
+ return schema.SingleNestedAttribute{
+ CustomType: basetypes.ObjectType{
+ AttrTypes: attributeTypes(),
+ },
+ Description: "The RNG (Random Number Generator) configuration.",
+ Optional: true,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "source": schema.StringAttribute{
+ Description: "The entropy source for the RNG device.",
+ Optional: true,
+ Computed: true,
+ },
+ "max_bytes": schema.Int64Attribute{
+ Description: "Maximum bytes of entropy allowed to get injected into the guest every period.",
+ Optional: true,
+ Computed: true,
+ },
+ "period": schema.Int64Attribute{
+ Description: "Period in milliseconds to limit entropy injection to the guest.",
+ Optional: true,
+ Computed: true,
+ },
+ },
+ }
+}
diff --git a/fwprovider/vm/rng/model.go b/fwprovider/vm/rng/model.go
new file mode 100644
index 00000000..e8259017
--- /dev/null
+++ b/fwprovider/vm/rng/model.go
@@ -0,0 +1,27 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package rng
+
+import (
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+// Model represents the RNG model.
+type Model struct {
+ Source types.String `tfsdk:"source"`
+ MaxBytes types.Int64 `tfsdk:"max_bytes"`
+ Period types.Int64 `tfsdk:"period"`
+}
+
+func attributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "source": types.StringType,
+ "max_bytes": types.Int64Type,
+ "period": types.Int64Type,
+ }
+}
diff --git a/fwprovider/vm/rng/resource.go b/fwprovider/vm/rng/resource.go
new file mode 100644
index 00000000..99ebc618
--- /dev/null
+++ b/fwprovider/vm/rng/resource.go
@@ -0,0 +1,147 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package rng
+
+import (
+ "context"
+ "reflect"
+
+ "github.com/hashicorp/terraform-plugin-framework/diag"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+
+ "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
+ "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms"
+)
+
+// Value represents the type for RNG settings.
+type Value = types.Object
+
+// NewValue returns a new Value with the given RNG settings from the PVE API.
+func NewValue(ctx context.Context, config *vms.GetResponseData, diags *diag.Diagnostics) Value {
+ rng := Model{}
+
+ if config.RNGDevice != nil {
+ rng.Source = types.StringValue(config.RNGDevice.Source)
+
+ if config.RNGDevice.MaxBytes != nil {
+ rng.MaxBytes = types.Int64Value(int64(*config.RNGDevice.MaxBytes))
+ }
+
+ if config.RNGDevice.Period != nil {
+ rng.Period = types.Int64Value(int64(*config.RNGDevice.Period))
+ }
+ }
+
+ obj, d := types.ObjectValueFrom(ctx, attributeTypes(), rng)
+ diags.Append(d...)
+
+ return obj
+}
+
+// createRNGDevice creates a new CustomRNGDevice from the given Model.
+func createRNGDevice(model Model, setSource bool) *vms.CustomRNGDevice {
+ rngDevice := &vms.CustomRNGDevice{}
+
+ if setSource && !model.Source.IsUnknown() {
+ rngDevice.Source = model.Source.ValueString()
+ }
+
+ if !model.MaxBytes.IsUnknown() && model.MaxBytes.ValueInt64() != 0 {
+ maxBytes := int(model.MaxBytes.ValueInt64())
+ rngDevice.MaxBytes = &maxBytes
+ }
+
+ if !model.Period.IsUnknown() && model.Period.ValueInt64() != 0 {
+ period := int(model.Period.ValueInt64())
+ rngDevice.Period = &period
+ }
+
+ return rngDevice
+}
+
+// FillCreateBody fills the CreateRequestBody with the RNG settings from the Value.
+//
+// In the 'create' context, v is the plan.
+func FillCreateBody(ctx context.Context, planValue Value, body *vms.CreateRequestBody, diags *diag.Diagnostics) {
+ var plan Model
+
+ if planValue.IsNull() || planValue.IsUnknown() {
+ return
+ }
+
+ d := planValue.As(ctx, &plan, basetypes.ObjectAsOptions{})
+ diags.Append(d...)
+
+ if d.HasError() {
+ return
+ }
+
+ rngDevice := createRNGDevice(plan, true)
+
+ if !reflect.DeepEqual(rngDevice, &vms.CustomRNGDevice{}) {
+ body.RNGDevice = rngDevice
+ }
+}
+
+// FillUpdateBody fills the UpdateRequestBody with the RNG settings from the Value.
+//
+// In the 'update' context, v is the plan and stateValue is the current state.
+func FillUpdateBody(
+ ctx context.Context,
+ planValue, stateValue Value,
+ updateBody *vms.UpdateRequestBody,
+ isClone bool,
+ diags *diag.Diagnostics,
+) {
+ var plan, state Model
+
+ if planValue.IsNull() || planValue.IsUnknown() || planValue.Equal(stateValue) {
+ return
+ }
+
+ d := planValue.As(ctx, &plan, basetypes.ObjectAsOptions{})
+ diags.Append(d...)
+ d = stateValue.As(ctx, &state, basetypes.ObjectAsOptions{})
+ diags.Append(d...)
+
+ if diags.HasError() {
+ return
+ }
+
+ rngDevice := createRNGDevice(state, true)
+
+ if !plan.Source.Equal(state.Source) {
+ if attribute.ShouldBeRemoved(plan.Source, state.Source, isClone) {
+ rngDevice.Source = ""
+ } else if attribute.IsDefined(plan.Source) {
+ rngDevice.Source = plan.Source.ValueString()
+ }
+ }
+
+ if !plan.MaxBytes.Equal(state.MaxBytes) {
+ if attribute.ShouldBeRemoved(plan.MaxBytes, state.MaxBytes, isClone) {
+ rngDevice.MaxBytes = nil
+ } else if attribute.IsDefined(plan.MaxBytes) {
+ maxBytes := int(plan.MaxBytes.ValueInt64())
+ rngDevice.MaxBytes = &maxBytes
+ }
+ }
+
+ if !plan.Period.Equal(state.Period) {
+ if attribute.ShouldBeRemoved(plan.Period, state.Period, isClone) {
+ rngDevice.Period = nil
+ } else if attribute.IsDefined(plan.Period) {
+ period := int(plan.Period.ValueInt64())
+ rngDevice.Period = &period
+ }
+ }
+
+ if !reflect.DeepEqual(rngDevice, &vms.CustomRNGDevice{}) {
+ updateBody.RNGDevice = rngDevice
+ }
+}
diff --git a/fwprovider/vm/rng/resource_schema.go b/fwprovider/vm/rng/resource_schema.go
new file mode 100644
index 00000000..809e7fca
--- /dev/null
+++ b/fwprovider/vm/rng/resource_schema.go
@@ -0,0 +1,70 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package rng
+
+import (
+ "github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+)
+
+// ResourceSchema defines the schema for the RNG resource.
+func ResourceSchema() schema.Attribute {
+ return schema.SingleNestedAttribute{
+ CustomType: basetypes.ObjectType{
+ AttrTypes: attributeTypes(),
+ },
+ Description: "The RNG (Random Number Generator) configuration. Can only be set by `root@pam.`",
+ MarkdownDescription: "Configure the RNG (Random Number Generator) device. The RNG device provides entropy " +
+ "to guests to ensure good quality random numbers for guest applications that require them. " +
+ "Can only be set by `root@pam.`" +
+ "See the [Proxmox documentation](https://pve.proxmox.com/pve-docs/pve-admin-guide.html#qm_virtual_machines_settings) " +
+ "for more information.",
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.Object{
+ objectplanmodifier.UseStateForUnknown(),
+ },
+ Attributes: map[string]schema.Attribute{
+ "source": schema.StringAttribute{
+ Description: "The entropy source for the RNG device.",
+ MarkdownDescription: "The file on the host to gather entropy from. " +
+ "In most cases, `/dev/urandom` should be preferred over `/dev/random` " +
+ "to avoid entropy-starvation issues on the host.",
+ Optional: true,
+ Computed: true,
+ Validators: []validator.String{
+ stringvalidator.LengthAtLeast(1),
+ },
+ },
+ "max_bytes": schema.Int64Attribute{
+ Description: "Maximum bytes of entropy allowed to get injected into the guest every period.",
+ MarkdownDescription: "Maximum bytes of entropy allowed to get injected into the guest every period. " +
+ "Use 0 to disable limiting (potentially dangerous).",
+ Optional: true,
+ Computed: true,
+ Validators: []validator.Int64{
+ int64validator.AtLeast(0),
+ },
+ },
+ "period": schema.Int64Attribute{
+ Description: "Period in milliseconds to limit entropy injection to the guest.",
+ MarkdownDescription: "Period in milliseconds to limit entropy injection to the guest. " +
+ "Use 0 to disable limiting (potentially dangerous).",
+ Optional: true,
+ Computed: true,
+ Validators: []validator.Int64{
+ int64validator.AtLeast(0),
+ },
+ },
+ },
+ }
+}
diff --git a/fwprovider/vm/rng/resource_test.go b/fwprovider/vm/rng/resource_test.go
new file mode 100644
index 00000000..07f0ea91
--- /dev/null
+++ b/fwprovider/vm/rng/resource_test.go
@@ -0,0 +1,167 @@
+//go:build acceptance || all
+
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package rng_test
+
+import (
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+
+ "github.com/bpg/terraform-provider-proxmox/fwprovider/test"
+)
+
+func TestAccResourceVM2RNG(t *testing.T) {
+ t.Parallel()
+
+ te := test.InitEnvironment(t)
+
+ tests := []struct {
+ name string
+ steps []resource.TestStep
+ }{
+ {"create VM with no rng params", []resource.TestStep{{
+ Config: te.RenderConfig(`
+ resource "proxmox_virtual_environment_vm2" "test_vm" {
+ node_name = "{{.NodeName}}"
+ name = "test-rng"
+ }`),
+ Check: test.NoResourceAttributesSet("proxmox_virtual_environment_vm2.test_vm", []string{
+ "rng.source",
+ "rng.max_bytes",
+ "rng.period",
+ }),
+ }}},
+ {"create VM with some rng params", []resource.TestStep{{
+ Config: te.RenderConfig(`
+ resource "proxmox_virtual_environment_vm2" "test_vm" {
+ node_name = "{{.NodeName}}"
+ name = "test-rng"
+ rng = {
+ source = "/dev/urandom"
+ }
+ }`, test.WithRootUser()),
+ Check: resource.ComposeTestCheckFunc(
+ test.ResourceAttributes("proxmox_virtual_environment_vm2.test_vm", map[string]string{
+ "rng.source": "/dev/urandom",
+ }),
+ test.NoResourceAttributesSet("proxmox_virtual_environment_vm2.test_vm", []string{
+ "rng.max_bytes",
+ "rng.period",
+ }),
+ ),
+ }}},
+ {"create VM with RNG params and then update them", []resource.TestStep{
+ {
+ Config: te.RenderConfig(`
+ resource "proxmox_virtual_environment_vm2" "test_vm" {
+ node_name = "{{.NodeName}}"
+ name = "test-rng"
+ rng = {
+ source = "/dev/urandom"
+ max_bytes = 1024
+ }
+ }`, test.WithRootUser()),
+ Check: resource.ComposeTestCheckFunc(
+ test.ResourceAttributes("proxmox_virtual_environment_vm2.test_vm", map[string]string{
+ "rng.source": "/dev/urandom",
+ "rng.max_bytes": "1024",
+ }),
+ test.NoResourceAttributesSet("proxmox_virtual_environment_vm2.test_vm", []string{
+ "rng.period",
+ }),
+ ),
+ },
+ { // now update the rng params and check if they are updated
+ Config: te.RenderConfig(`
+ resource "proxmox_virtual_environment_vm2" "test_vm" {
+ node_name = "{{.NodeName}}"
+ name = "test-rng"
+ rng = {
+ source = "/dev/random"
+ period = 1000
+ }
+ }`, test.WithRootUser()),
+ Check: resource.ComposeTestCheckFunc(
+ test.ResourceAttributes("proxmox_virtual_environment_vm2.test_vm", map[string]string{
+ "rng.source": "/dev/random",
+ "rng.period": "1000",
+ }),
+ test.NoResourceAttributesSet("proxmox_virtual_environment_vm2.test_vm", []string{
+ "rng.max_bytes",
+ }),
+ ),
+ },
+ {
+ RefreshState: true,
+ },
+ }},
+ {"clone VM with some rng params", []resource.TestStep{{
+ Config: te.RenderConfig(`
+ resource "proxmox_virtual_environment_vm2" "template_vm" {
+ node_name = "{{.NodeName}}"
+ name = "template-rng"
+ rng = {
+ source = "/dev/urandom"
+ max_bytes = 1024
+ }
+ }
+ resource "proxmox_virtual_environment_vm2" "test_vm" {
+ node_name = "{{.NodeName}}"
+ name = "test-rng"
+ clone = {
+ id = proxmox_virtual_environment_vm2.template_vm.id
+ }
+ }`, test.WithRootUser()),
+ Check: resource.ComposeTestCheckFunc(
+ test.ResourceAttributes("proxmox_virtual_environment_vm2.test_vm", map[string]string{
+ "rng.source": "/dev/urandom",
+ "rng.max_bytes": "1024",
+ }),
+ ),
+ }}},
+ {"clone VM with some rng params and updating them in the clone", []resource.TestStep{{
+ Config: te.RenderConfig(`
+ resource "proxmox_virtual_environment_vm2" "template_vm" {
+ node_name = "{{.NodeName}}"
+ name = "template-rng"
+ rng = {
+ source = "/dev/urandom"
+ max_bytes = 1024
+ }
+ }
+ resource "proxmox_virtual_environment_vm2" "test_vm" {
+ node_name = "{{.NodeName}}"
+ name = "test-rng"
+ clone = {
+ id = proxmox_virtual_environment_vm2.template_vm.id
+ }
+ rng = {
+ source = "/dev/random"
+ period = 2000
+ }
+ }`, test.WithRootUser()),
+ Check: resource.ComposeTestCheckFunc(
+ test.ResourceAttributes("proxmox_virtual_environment_vm2.test_vm", map[string]string{
+ "rng.source": "/dev/random",
+ "rng.period": "2000",
+ "rng.max_bytes": "1024",
+ }),
+ ),
+ }}},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ resource.ParallelTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: te.AccProviders,
+ Steps: tt.steps,
+ })
+ })
+ }
+}
diff --git a/fwprovider/vm/vga/datasource_schema.go b/fwprovider/vm/vga/datasource_schema.go
index d0d2f6b7..6ed7e93c 100644
--- a/fwprovider/vm/vga/datasource_schema.go
+++ b/fwprovider/vm/vga/datasource_schema.go
@@ -1,3 +1,9 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
package vga
import (
diff --git a/fwprovider/vm/vga/model.go b/fwprovider/vm/vga/model.go
index 6dc310b4..d8a13b3f 100644
--- a/fwprovider/vm/vga/model.go
+++ b/fwprovider/vm/vga/model.go
@@ -1,3 +1,9 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
package vga
import (
diff --git a/fwprovider/vm/vga/resource_schema.go b/fwprovider/vm/vga/resource_schema.go
index fc553133..bb3506f2 100644
--- a/fwprovider/vm/vga/resource_schema.go
+++ b/fwprovider/vm/vga/resource_schema.go
@@ -1,3 +1,9 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
package vga
import (
diff --git a/proxmox/nodes/vms/custom_rng_device.go b/proxmox/nodes/vms/custom_rng_device.go
new file mode 100644
index 00000000..1745b1a3
--- /dev/null
+++ b/proxmox/nodes/vms/custom_rng_device.go
@@ -0,0 +1,86 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package vms
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/url"
+ "strconv"
+ "strings"
+)
+
+// CustomRNGDevice represents a random number generator device configuration.
+type CustomRNGDevice struct {
+ Source string `json:"source,omitempty" url:"source,omitempty"`
+ MaxBytes *int `json:"max_bytes,omitempty" url:"max_bytes,omitempty"`
+ Period *int `json:"period,omitempty" url:"period,omitempty"`
+}
+
+// EncodeValues converts a CustomRNGDevice struct to a URL value.
+func (r *CustomRNGDevice) EncodeValues(key string, v *url.Values) error {
+ var values []string
+
+ if r.Source != "" {
+ values = append(values, fmt.Sprintf("source=%s", r.Source))
+ }
+
+ if r.MaxBytes != nil {
+ values = append(values, fmt.Sprintf("max_bytes=%d", *r.MaxBytes))
+ }
+
+ if r.Period != nil {
+ values = append(values, fmt.Sprintf("period=%d", *r.Period))
+ }
+
+ if len(values) > 0 {
+ v.Add(key, strings.Join(values, ","))
+ }
+
+ return nil
+}
+
+// UnmarshalJSON unmarshals a JSON object into a CustomRNGDevice struct.
+func (r *CustomRNGDevice) UnmarshalJSON(b []byte) error {
+ var s string
+
+ if err := json.Unmarshal(b, &s); err != nil {
+ return fmt.Errorf("failed to unmarshal CustomRNGDevice: %w", err)
+ }
+
+ pairs := strings.Split(s, ",")
+
+ for _, p := range pairs {
+ v := strings.Split(strings.TrimSpace(p), "=")
+ if len(v) == 1 {
+ r.Source = v[0]
+ } else if len(v) == 2 {
+ switch v[0] {
+ case "source":
+ r.Source = v[1]
+
+ case "max_bytes":
+ maxBytes, err := strconv.Atoi(v[1])
+ if err != nil {
+ return fmt.Errorf("failed to parse max_bytes: %w", err)
+ }
+
+ r.MaxBytes = &maxBytes
+
+ case "period":
+ period, err := strconv.Atoi(v[1])
+ if err != nil {
+ return fmt.Errorf("failed to parse period: %w", err)
+ }
+
+ r.Period = &period
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/proxmox/nodes/vms/custom_rng_device_test.go b/proxmox/nodes/vms/custom_rng_device_test.go
new file mode 100644
index 00000000..9fc99af8
--- /dev/null
+++ b/proxmox/nodes/vms/custom_rng_device_test.go
@@ -0,0 +1,158 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package vms
+
+import (
+ "net/url"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/bpg/terraform-provider-proxmox/proxmox/helpers/ptr"
+)
+
+func TestCustomRNGDevice_UnmarshalJSON(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ line string
+ want *CustomRNGDevice
+ wantErr bool
+ }{
+ {
+ name: "source only",
+ line: `"source=urandom"`,
+ want: &CustomRNGDevice{
+ Source: "urandom",
+ },
+ },
+ {
+ name: "all options",
+ line: `"source=/dev/random,max_bytes=1024,period=1000"`,
+ want: &CustomRNGDevice{
+ Source: "/dev/random",
+ MaxBytes: ptr.Ptr(1024),
+ Period: ptr.Ptr(1000),
+ },
+ },
+ {
+ name: "source with max_bytes",
+ line: `"source=urandom,max_bytes=2048"`,
+ want: &CustomRNGDevice{
+ Source: "urandom",
+ MaxBytes: ptr.Ptr(2048),
+ },
+ },
+ {
+ name: "source with period",
+ line: `"source=urandom,period=2000"`,
+ want: &CustomRNGDevice{
+ Source: "urandom",
+ Period: ptr.Ptr(2000),
+ },
+ },
+ {
+ name: "invalid JSON",
+ line: `{"source": "urandom"}`,
+ wantErr: true,
+ },
+ {
+ name: "invalid max_bytes",
+ line: `"source=urandom,max_bytes=invalid"`,
+ wantErr: true,
+ },
+ {
+ name: "invalid period",
+ line: `"source=urandom,period=invalid"`,
+ wantErr: true,
+ },
+ {
+ name: "single value source",
+ line: `"urandom"`,
+ want: &CustomRNGDevice{
+ Source: "urandom",
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ r := &CustomRNGDevice{}
+ err := r.UnmarshalJSON([]byte(tt.line))
+
+ if tt.wantErr {
+ require.Error(t, err)
+ return
+ }
+
+ require.NoError(t, err)
+ require.Equal(t, tt.want, r)
+ })
+ }
+}
+
+func TestCustomRNGDevice_EncodeValues(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ device *CustomRNGDevice
+ key string
+ expected string
+ }{
+ {
+ name: "source only",
+ device: &CustomRNGDevice{
+ Source: "urandom",
+ },
+ key: "rng0",
+ expected: "source=urandom",
+ },
+ {
+ name: "all options",
+ device: &CustomRNGDevice{
+ Source: "/dev/random",
+ MaxBytes: ptr.Ptr(1024),
+ Period: ptr.Ptr(1000),
+ },
+ key: "rng0",
+ expected: "source=/dev/random,max_bytes=1024,period=1000",
+ },
+ {
+ name: "source with max_bytes",
+ device: &CustomRNGDevice{
+ Source: "urandom",
+ MaxBytes: ptr.Ptr(2048),
+ },
+ key: "rng0",
+ expected: "source=urandom,max_bytes=2048",
+ },
+ {
+ name: "source with period",
+ device: &CustomRNGDevice{
+ Source: "urandom",
+ Period: ptr.Ptr(2000),
+ },
+ key: "rng0",
+ expected: "source=urandom,period=2000",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ values := &url.Values{}
+ err := tt.device.EncodeValues(tt.key, values)
+ require.NoError(t, err)
+ require.Equal(t, tt.expected, values.Get(tt.key))
+ })
+ }
+}
diff --git a/proxmox/nodes/vms/vms_types.go b/proxmox/nodes/vms/vms_types.go
index 02b82e2e..cce2d6cf 100644
--- a/proxmox/nodes/vms/vms_types.go
+++ b/proxmox/nodes/vms/vms_types.go
@@ -44,13 +44,13 @@ type CreateRequestBody struct {
Boot *CustomBoot `json:"boot,omitempty" url:"boot,omitempty"`
CDROM *string `json:"cdrom,omitempty" url:"cdrom,omitempty"`
CloudInitConfig *CustomCloudInitConfig `json:"cloudinit,omitempty" url:"cloudinit,omitempty"`
+ CPUAffinity *string `json:"affinity,omitempty" url:"affinity,omitempty"`
CPUArchitecture *string `json:"arch,omitempty" url:"arch,omitempty"`
CPUCores *int64 `json:"cores,omitempty" url:"cores,omitempty"`
CPUEmulation *CustomCPUEmulation `json:"cpu,omitempty" url:"cpu,omitempty"`
CPULimit *int64 `json:"cpulimit,omitempty" url:"cpulimit,omitempty"`
CPUSockets *int64 `json:"sockets,omitempty" url:"sockets,omitempty"`
CPUUnits *int64 `json:"cpuunits,omitempty" url:"cpuunits,omitempty"`
- CPUAffinity *string `json:"affinity,omitempty" url:"affinity,omitempty"`
DedicatedMemory *int `json:"memory,omitempty" url:"memory,omitempty"`
Delete []string `json:"delete,omitempty" url:"delete,omitempty,comma"`
DeletionProtection *types.CustomBool `json:"protection,omitempty" url:"protection,omitempty,int"`
@@ -80,6 +80,7 @@ type CreateRequestBody struct {
PCIDevices CustomPCIDevices `json:"hostpci,omitempty" url:"hostpci,omitempty"`
PoolID *string `json:"pool,omitempty" url:"pool,omitempty"`
Revert *string `json:"revert,omitempty" url:"revert,omitempty"`
+ RNGDevice *CustomRNGDevice `json:"rng0,omitempty" url:"rng0,omitempty"`
SCSIHardware *string `json:"scsihw,omitempty" url:"scsihw,omitempty"`
SerialDevices CustomSerialDevices `json:"serial,omitempty" url:"serial,omitempty"`
SharedMemory *CustomSharedMemory `json:"ivshmem,omitempty" url:"ivshmem,omitempty"`
@@ -290,6 +291,7 @@ type GetResponseData struct {
Overwrite *types.CustomBool `json:"force,omitempty"`
PoolID *string `json:"pool,omitempty"`
Revert *string `json:"revert,omitempty"`
+ RNGDevice *CustomRNGDevice `json:"rng0,omitempty"`
SCSIHardware *string `json:"scsihw,omitempty"`
SerialDevice0 *string `json:"serial0,omitempty"`
SerialDevice1 *string `json:"serial1,omitempty"`
diff --git a/proxmoxtf/resource/vm/vm.go b/proxmoxtf/resource/vm/vm.go
index 2c94e722..e2464bcd 100644
--- a/proxmoxtf/resource/vm/vm.go
+++ b/proxmoxtf/resource/vm/vm.go
@@ -104,6 +104,8 @@ const (
dvOperatingSystemType = "other"
dvPoolID = ""
dvProtection = false
+ dvRNGMaxBytes = 1024
+ dvRNGPeriod = 1000
dvSerialDeviceDevice = "socket"
dvSMBIOSFamily = ""
dvSMBIOSManufacturer = ""
@@ -244,6 +246,10 @@ const (
mkOperatingSystemType = "type"
mkPoolID = "pool_id"
mkProtection = "protection"
+ mkRNG = "rng"
+ mkRNGSource = "source"
+ mkRNGMaxBytes = "max_bytes"
+ mkRNGPeriod = "period"
mkSerialDevice = "serial_device"
mkSerialDeviceDevice = "device"
mkSMBIOS = "smbios"
@@ -1205,6 +1211,42 @@ func VM() *schema.Resource {
Optional: true,
Default: dvProtection,
},
+ mkRNG: {
+ Type: schema.TypeList,
+ Description: "The RNG configuration",
+ Optional: true,
+ DefaultFunc: func() (interface{}, error) {
+ return []interface{}{}, nil
+ },
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ mkRNGSource: {
+ Type: schema.TypeString,
+ Description: "The file on the host to gather entropy from. " +
+ "In most cases, `/dev/urandom` should be preferred over `/dev/random` " +
+ "to avoid entropy-starvation issues on the host.",
+ ValidateFunc: validation.StringIsNotEmpty,
+ Required: true,
+ },
+ mkRNGMaxBytes: {
+ Type: schema.TypeInt,
+ Description: "Maximum bytes of entropy allowed to get injected into the guest every `period` " +
+ "milliseconds. Prefer a lower value when using `/dev/random` as source.",
+ Optional: true,
+ Computed: true,
+ ValidateDiagFunc: validation.ToDiagFunc(validation.IntAtLeast(1)),
+ },
+ mkRNGPeriod: {
+ Type: schema.TypeInt,
+ Description: "Every `period` milliseconds the entropy-injection quota is reset, " +
+ "allowing the guest to retrieve another `max_bytes` of entropy.",
+ Optional: true,
+ Computed: true,
+ ValidateDiagFunc: validation.ToDiagFunc(validation.IntAtLeast(1)),
+ },
+ },
+ },
+ },
mkSerialDevice: {
Type: schema.TypeList,
Description: "The serial devices",
@@ -2409,25 +2451,13 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
}
}
- var tpmState *vms.CustomTPMState
-
- tpmStateBlock := d.Get(mkTPMState).([]interface{})
- if len(tpmStateBlock) > 0 && tpmStateBlock[0] != nil {
- block := tpmStateBlock[0].(map[string]interface{})
-
- datastoreID, _ := block[mkTPMStateDatastoreID].(string)
- version, _ := block[mkTPMStateVersion].(string)
-
- if version == "" {
- version = dvTPMStateVersion
- }
-
- tpmState = &vms.CustomTPMState{
- FileVolume: fmt.Sprintf("%s:1", datastoreID),
- Version: &version,
- }
+ tpmState := vmGetTPMState(d, nil)
+ if tpmState != nil && (tpmState.Version == nil || *tpmState.Version == "") {
+ tpmState.Version = ptr.Ptr(dvTPMStateVersion)
}
+ rng := vmGetRNGDevice(d)
+
initializationConfig := vmGetCloudInitConfig(d)
initializationAttr := d.Get(mkInitialization)
@@ -2655,6 +2685,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
NUMADevices: numaDeviceObjects,
OSType: &operatingSystemType,
PCIDevices: pciDeviceObjects,
+ RNGDevice: rng,
SCSIHardware: &scsiHardware,
SerialDevices: serialDevices,
SharedMemory: memorySharedObject,
@@ -3048,6 +3079,36 @@ func vmGetTPMStateAsStorageDevice(d *schema.ResourceData, disk []interface{}) *v
return storageDevice
}
+func vmGetRNGDevice(d *schema.ResourceData) *vms.CustomRNGDevice {
+ rngBlock := d.Get(mkRNG).([]interface{})
+
+ var rng *vms.CustomRNGDevice
+
+ if len(rngBlock) > 0 && rngBlock[0] != nil {
+ block := rngBlock[0].(map[string]interface{})
+
+ source, _ := block[mkRNGSource].(string)
+
+ maxBytes, _ := block[mkRNGMaxBytes].(int)
+ if maxBytes == 0 {
+ maxBytes = dvRNGMaxBytes
+ }
+
+ period, _ := block[mkRNGPeriod].(int)
+ if period == 0 {
+ period = dvRNGPeriod
+ }
+
+ rng = &vms.CustomRNGDevice{
+ Source: source,
+ MaxBytes: &maxBytes,
+ Period: &period,
+ }
+ }
+
+ return rng
+}
+
func vmGetHostPCIDeviceObjects(d *schema.ResourceData) vms.CustomPCIDevices {
pciDevice := d.Get(mkHostPCI).([]interface{})
pciDeviceObjects := make(vms.CustomPCIDevices, len(pciDevice))
@@ -3758,6 +3819,35 @@ func vmReadCustom(
}
}
+ if vmConfig.RNGDevice != nil {
+ rng := map[string]interface{}{}
+
+ rng[mkRNGSource] = vmConfig.RNGDevice.Source
+
+ if vmConfig.RNGDevice.MaxBytes != nil {
+ rng[mkRNGMaxBytes] = *vmConfig.RNGDevice.MaxBytes
+ }
+
+ if vmConfig.RNGDevice.Period != nil {
+ rng[mkRNGPeriod] = *vmConfig.RNGDevice.Period
+ }
+
+ currentRNG := d.Get(mkRNG).([]interface{})
+
+ if len(clone) > 0 {
+ if len(currentRNG) > 0 {
+ err := d.Set(mkRNG, []interface{}{rng})
+ diags = append(diags, diag.FromErr(err)...)
+ }
+ } else if len(currentRNG) > 0 ||
+ rng[mkRNGSource] != "" ||
+ rng[mkRNGMaxBytes] != dvRNGMaxBytes || // or != 0?
+ rng[mkRNGPeriod] != dvRNGPeriod {
+ err := d.Set(mkRNG, []interface{}{rng})
+ diags = append(diags, diag.FromErr(err)...)
+ }
+ }
+
currentPCIList := d.Get(mkHostPCI).([]interface{})
pciMap := map[string]interface{}{}
@@ -5039,6 +5129,15 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
rebootRequired = true
}
+ // Prepare the new RNG configuration.
+ if d.HasChange(mkRNG) {
+ rngDevice := vmGetRNGDevice(d)
+
+ updateBody.RNGDevice = rngDevice
+
+ rebootRequired = true
+ }
+
// Prepare the new cloud-init configuration.
stoppedBeforeUpdate := false