@@ -10,6 +10,7 @@ import (
1010 "github.com/dmacvicar/terraform-provider-libvirt/v2/internal/libvirt"
1111 "github.com/hashicorp/terraform-plugin-framework/attr"
1212 "github.com/hashicorp/terraform-plugin-framework/diag"
13+ "github.com/hashicorp/terraform-plugin-framework/path"
1314 "github.com/hashicorp/terraform-plugin-framework/resource"
1415 "github.com/hashicorp/terraform-plugin-framework/resource/schema"
1516 "github.com/hashicorp/terraform-plugin-framework/types"
@@ -19,8 +20,9 @@ import (
1920
2021// Ensure the implementation satisfies the expected interfaces
2122var (
22- _ resource.Resource = & DomainResource {}
23- _ resource.ResourceWithConfigure = & DomainResource {}
23+ _ resource.Resource = & DomainResource {}
24+ _ resource.ResourceWithConfigure = & DomainResource {}
25+ _ resource.ResourceWithImportState = & DomainResource {}
2426)
2527
2628// NewDomainResource creates a new domain resource
@@ -860,10 +862,21 @@ func (r *DomainResource) Read(ctx context.Context, req resource.ReadRequest, res
860862 return
861863 }
862864
863- planData , diags := prepareDomainPlan (ctx , & state )
864- resp .Diagnostics .Append (diags ... )
865- if resp .Diagnostics .HasError () {
866- return
865+ // Detect import: after ImportState, only UUID is set — Name will be null.
866+ // When importing, pass nil as plan so DomainFromXML populates all fields from XML.
867+ isImport := state .Name .IsNull () || state .Name .IsUnknown ()
868+
869+ var plan * generated.DomainModel
870+ var waitAttrs []attr.Value
871+
872+ if ! isImport {
873+ planData , diags := prepareDomainPlan (ctx , & state )
874+ resp .Diagnostics .Append (diags ... )
875+ if resp .Diagnostics .HasError () {
876+ return
877+ }
878+ plan = & planData .SanitizedModel
879+ waitAttrs = planData .WaitAttributes
867880 }
868881
869882 domain , err := r .client .LookupDomainByUUID (state .UUID .ValueString ())
@@ -890,7 +903,7 @@ func (r *DomainResource) Read(ctx context.Context, req resource.ReadRequest, res
890903 return
891904 }
892905
893- stateModel , err := generated .DomainFromXML (ctx , parsedDomain , & planData . SanitizedModel )
906+ stateModel , err := generated .DomainFromXML (ctx , parsedDomain , plan )
894907 if err != nil {
895908 resp .Diagnostics .AddError (
896909 "Failed to Convert Domain" ,
@@ -901,10 +914,15 @@ func (r *DomainResource) Read(ctx context.Context, req resource.ReadRequest, res
901914
902915 state .DomainModel = * stateModel
903916
904- state .Devices , diags = applyWaitForIPValues (ctx , state .Devices , planData .WaitAttributes )
905- resp .Diagnostics .Append (diags ... )
906- if resp .Diagnostics .HasError () {
907- return
917+ // Always apply wait_for_ip type conversion — the schema expects it.
918+ // During import, waitAttrs is nil so all interfaces get null wait_for_ip.
919+ {
920+ var diags diag.Diagnostics
921+ state .Devices , diags = applyWaitForIPValues (ctx , state .Devices , waitAttrs )
922+ resp .Diagnostics .Append (diags ... )
923+ if resp .Diagnostics .HasError () {
924+ return
925+ }
908926 }
909927
910928 if ! originalMetadata .IsNull () && ! originalMetadata .IsUnknown () {
@@ -915,7 +933,8 @@ func (r *DomainResource) Read(ctx context.Context, req resource.ReadRequest, res
915933 state .ID = originalID
916934 }
917935
918- if ! state .Autostart .IsNull () && ! state .Autostart .IsUnknown () {
936+ // Read autostart — always during import, conditionally otherwise
937+ if isImport || (! state .Autostart .IsNull () && ! state .Autostart .IsUnknown ()) {
919938 autostart , err := r .client .Libvirt ().DomainGetAutostart (domain )
920939 if err != nil {
921940 resp .Diagnostics .AddError (
@@ -927,9 +946,31 @@ func (r *DomainResource) Read(ctx context.Context, req resource.ReadRequest, res
927946 state .Autostart = types .BoolValue (autostart == 1 )
928947 }
929948
949+ // Read running state — always during import, preserve existing otherwise
950+ if isImport {
951+ domainState , _ , err := r .client .Libvirt ().DomainGetState (domain , 0 )
952+ if err != nil {
953+ resp .Diagnostics .AddError (
954+ "Failed to Get Domain State" ,
955+ "Failed to read domain running state: " + err .Error (),
956+ )
957+ return
958+ }
959+ state .Running = types .BoolValue (uint32 (domainState ) == uint32 (golibvirt .DomainRunning ))
960+ }
961+
930962 resp .Diagnostics .Append (resp .State .Set (ctx , & state )... )
931963}
932964
965+ // ImportState imports an existing libvirt domain by UUID.
966+ //
967+ // Usage:
968+ //
969+ // terraform import libvirt_domain.myvm <uuid>
970+ func (r * DomainResource ) ImportState (ctx context.Context , req resource.ImportStateRequest , resp * resource.ImportStateResponse ) {
971+ resource .ImportStatePassthroughID (ctx , path .Root ("uuid" ), req , resp )
972+ }
973+
933974// waitForDomainState waits for a domain to reach the specified state with a timeout
934975func waitForDomainState (client * libvirt.Client , domain golibvirt.Domain , targetState uint32 , timeout time.Duration ) error {
935976 deadline := time .Now ().Add (timeout )
0 commit comments