logging: Add support for `state migrate` message types (#210)
diff --git a/logging_state_migrate.go b/logging_state_migrate.go
new file mode 100644
index 0000000..00be0a4
--- /dev/null
+++ b/logging_state_migrate.go
@@ -0,0 +1,134 @@
+// Copyright IBM Corp. 2019, 2026
+// SPDX-License-Identifier: MPL-2.0
+
+package tfjson
+
+const (
+	// Provider installation messages
+	ProviderInstallationStart           LogMessageType = "provider_installation_start"
+	StateStoreProviderInstallationStart LogMessageType = "state_store_provider_installation_start"
+	ProviderQueryUsePreviousVersion     LogMessageType = "provider_query_use_previous_version"
+	ProviderQueryUsePreviousConstraints LogMessageType = "provider_query_use_constraints"
+	ProviderQueryUseLatest              LogMessageType = "provider_query_use_latest"
+	ProviderVersionAlreadyInstalled     LogMessageType = "provider_version_already_installed"
+	ProviderVersionFoundInCacheDir      LogMessageType = "provider_version_found_in_cache_dir"
+	ProviderVersionInstallationStart    LogMessageType = "provider_version_installation_start"
+	ProviderVersionInstallationComplete LogMessageType = "provider_version_installation_complete"
+	BuiltInProviderAvailable            LogMessageType = "built_in_provider_available"
+	ThirdPartyProvidersInstalled        LogMessageType = "third_party_providers_installed"
+
+	// Dependency lock file messages
+	ProviderLockfileCreated LogMessageType = "provider_lockfile_created"
+	ProviderLockfileUpdated LogMessageType = "provider_lockfile_updated"
+
+	// Provider trust-related messages
+	ProviderInteractiveApproval  LogMessageType = "provider_interactive_approval"
+	ProviderInteractiveRejection LogMessageType = "provider_interactive_rejection"
+	ProviderAutomaticApproval    LogMessageType = "provider_automatic_approval"
+
+	// State migration-related messages
+	LogMigrationStart                             LogMessageType = "migration_start"
+	LogMigrationComplete                          LogMessageType = "migration_complete"
+	LogMigrationErrored                           LogMessageType = "migration_errored"
+	LogMigrationFinalized                         LogMessageType = "migration_finalized"
+	LogMigrationSourceInitializationStart         LogMessageType = "migration_source_initialization_start"
+	LogMigrationSourceInitializationComplete      LogMessageType = "migration_source_initialization_complete"
+	LogMigrationDestinationInitializationStart    LogMessageType = "migration_destination_initialization_start"
+	LogMigrationDestinationInitializationComplete LogMessageType = "migration_destination_initialization_complete"
+)
+
+type ProviderInstallationStartMessage struct {
+	baseLogMessage
+}
+
+type StateStoreProviderInstallationStartMessage struct {
+	baseLogMessage
+}
+
+type ProviderQueryUsePreviousVersionMessage struct {
+	baseLogMessage
+}
+
+type ProviderQueryUsePreviousConstraintsMessage struct {
+	baseLogMessage
+}
+
+type ProviderQueryUseLatestMessage struct {
+	baseLogMessage
+}
+
+type ProviderVersionAlreadyInstalledMessage struct {
+	baseLogMessage
+}
+
+type ProviderVersionFoundInCacheDirMessage struct {
+	baseLogMessage
+}
+
+type ProviderVersionInstallationStartMessage struct {
+	baseLogMessage
+}
+
+type ProviderVersionInstallationCompleteMessage struct {
+	baseLogMessage
+}
+
+type BuiltInProviderAvailableMessage struct {
+	baseLogMessage
+}
+
+type ThirdPartyProvidersInstalledMessage struct {
+	baseLogMessage
+}
+
+type ProviderLockfileCreatedMessage struct {
+	baseLogMessage
+}
+
+type ProviderLockfileUpdatedMessage struct {
+	baseLogMessage
+}
+
+type ProviderInteractiveApprovalMessage struct {
+	baseLogMessage
+}
+
+type ProviderInteractiveRejectionMessage struct {
+	baseLogMessage
+}
+
+type ProviderAutomaticApprovalMessage struct {
+	baseLogMessage
+}
+
+type MigrationStartMessage struct {
+	baseLogMessage
+}
+
+type MigrationCompleteMessage struct {
+	baseLogMessage
+}
+
+type MigrationErroredMessage struct {
+	baseLogMessage
+}
+
+type MigrationFinalizedMessage struct {
+	baseLogMessage
+}
+
+type MigrationSourceInitializationStartMessage struct {
+	baseLogMessage
+}
+
+type MigrationSourceInitializationCompleteMessage struct {
+	baseLogMessage
+}
+
+type MigrationDestinationInitializationStartMessage struct {
+	baseLogMessage
+}
+
+type MigrationDestinationInitializationCompleteMessage struct {
+	baseLogMessage
+}
diff --git a/logging_test.go b/logging_test.go
index 7f9f6d5..91abc52 100644
--- a/logging_test.go
+++ b/logging_test.go
@@ -346,3 +346,255 @@
 		}
 	}
 }
+
+func TestLogging_stateMigrate(t *testing.T) {
+	testCases := []struct {
+		rawMessage      string
+		expectedMessage LogMsg
+	}{
+		// provider installation
+		{
+			`{"@level":"info","@message":"Installing providers...","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_installation_start"}`,
+			ProviderInstallationStartMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "Installing providers...",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"Installing provider hashicorp/test v1.2.3 for state store test_foo...","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"state_store_provider_installation_start"}`,
+			StateStoreProviderInstallationStartMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "Installing provider hashicorp/test v1.2.3 for state store test_foo...",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"hashicorp/test: Reusing version 1.0.0 from the dependency lock file","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_query_use_previous_version"}`,
+			ProviderQueryUsePreviousVersionMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "hashicorp/test: Reusing version 1.0.0 from the dependency lock file",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"Finding matching versions for provider: hashicorp/test, version_constraint: \"1.0.0\"","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_query_use_constraints"}`,
+			ProviderQueryUsePreviousConstraintsMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "Finding matching versions for provider: hashicorp/test, version_constraint: \"1.0.0\"",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"hashicorp/test: Finding latest version...","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_query_use_latest"}`,
+			ProviderQueryUseLatestMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "hashicorp/test: Finding latest version...",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"hashicorp/test v1.0.0: Using previously-installed provider version","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_version_already_installed"}`,
+			ProviderVersionAlreadyInstalledMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "hashicorp/test v1.0.0: Using previously-installed provider version",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"hashicorp/test v1.0.0: Using from the shared cache directory","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_version_found_in_cache_dir"}`,
+			ProviderVersionFoundInCacheDirMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "hashicorp/test v1.0.0: Using from the shared cache directory",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"Installing provider version: hashicorp/test v1.2.3...","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_version_installation_start"}`,
+			ProviderVersionInstallationStartMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "Installing provider version: hashicorp/test v1.2.3...",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"Installed provider version: hashicorp/test v1.2.3 (verified checksum)","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_version_installation_complete"}`,
+			ProviderVersionInstallationCompleteMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "Installed provider version: hashicorp/test v1.2.3 (verified checksum)",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"hashicorp/test is built in to Terraform","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"built_in_provider_available"}`,
+			BuiltInProviderAvailableMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "hashicorp/test is built in to Terraform",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"Partner and community providers are signed by their developers.","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"third_party_providers_installed"}`,
+			ThirdPartyProvidersInstalledMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "Partner and community providers are signed by their developers.",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		// dependency lock file
+		{
+			`{"@level":"info","@message":"Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above.","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_lockfile_created"}`,
+			ProviderLockfileCreatedMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above.",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"Terraform has made some changes to the provider dependency selections recorded in the .terraform.lock.hcl file.","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_lockfile_updated"}`,
+			ProviderLockfileUpdatedMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "Terraform has made some changes to the provider dependency selections recorded in the .terraform.lock.hcl file.",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		// provider trust
+		{
+			`{"@level":"info","@message":"The state store provider was approved by the user.","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_interactive_approval"}`,
+			ProviderInteractiveApprovalMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "The state store provider was approved by the user.",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"The state store provider was rejected by the user.","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_interactive_rejection"}`,
+			ProviderInteractiveRejectionMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "The state store provider was rejected by the user.",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"The state store provider was approved automatically.","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_automatic_approval"}`,
+			ProviderAutomaticApprovalMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "The state store provider was approved automatically.",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		// state migration
+		{
+			`{"@level":"info","@message":"Initializing source...","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"migration_source_initialization_start"}`,
+			MigrationSourceInitializationStartMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "Initializing source...",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"Initialized source.","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"migration_source_initialization_complete"}`,
+			MigrationSourceInitializationCompleteMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "Initialized source.",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"Initializing destination...","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"migration_destination_initialization_start"}`,
+			MigrationDestinationInitializationStartMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "Initializing destination...",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"Initialized destination.","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"migration_destination_initialization_complete"}`,
+			MigrationDestinationInitializationCompleteMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "Initialized destination.",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"Migrating state from backend \"local\" to backend \"local\"...","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"migration_start"}`,
+			MigrationStartMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "Migrating state from backend \"local\" to backend \"local\"...",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"The migration process has copied state from the backend \"local\" to the backend \"local\"","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"migration_complete"}`,
+			MigrationCompleteMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "The migration process has copied state from the backend \"local\" to the backend \"local\"",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"Finished migrating state from backend \"local\" to backend \"local\".","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"migration_finalized"}`,
+			MigrationFinalizedMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "Finished migrating state from backend \"local\" to backend \"local\".",
+					Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC),
+				},
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		msg, err := UnmarshalLogMessage([]byte(tc.rawMessage))
+		if err != nil {
+			t.Fatal(err)
+		}
+		if diff := cmp.Diff(tc.expectedMessage, msg, cmpOpts); diff != "" {
+			t.Fatalf("unexpected message: %s", diff)
+		}
+	}
+}
diff --git a/logging_types.go b/logging_types.go
index da149ba..3e905f6 100644
--- a/logging_types.go
+++ b/logging_types.go
@@ -30,6 +30,36 @@
 	ListStartMessage{},
 	ListResourceFoundMessage{},
 	ListCompleteMessage{},
+
+	// state migrate
+	// provider installation
+	ProviderInstallationStartMessage{},
+	StateStoreProviderInstallationStartMessage{},
+	ProviderQueryUsePreviousVersionMessage{},
+	ProviderQueryUsePreviousConstraintsMessage{},
+	ProviderQueryUseLatestMessage{},
+	ProviderVersionAlreadyInstalledMessage{},
+	ProviderVersionFoundInCacheDirMessage{},
+	ProviderVersionInstallationStartMessage{},
+	ProviderVersionInstallationCompleteMessage{},
+	BuiltInProviderAvailableMessage{},
+	ThirdPartyProvidersInstalledMessage{},
+	// dependency lock file
+	ProviderLockfileCreatedMessage{},
+	ProviderLockfileUpdatedMessage{},
+	// Provider trust-related message
+	ProviderInteractiveApprovalMessage{},
+	ProviderInteractiveRejectionMessage{},
+	ProviderAutomaticApprovalMessage{},
+	// state migration
+	MigrationStartMessage{},
+	MigrationCompleteMessage{},
+	MigrationErroredMessage{},
+	MigrationFinalizedMessage{},
+	MigrationSourceInitializationStartMessage{},
+	MigrationSourceInitializationCompleteMessage{},
+	MigrationDestinationInitializationStartMessage{},
+	MigrationDestinationInitializationCompleteMessage{},
 }
 
 func unmarshalByType(t LogMessageType, b []byte) (LogMsg, error) {
@@ -66,6 +96,84 @@
 	case MessageListComplete:
 		v := ListCompleteMessage{}
 		return v, d.Decode(&v)
+
+	// state migrate
+	// provider installation
+	case ProviderInstallationStart:
+		v := ProviderInstallationStartMessage{}
+		return v, d.Decode(&v)
+	case StateStoreProviderInstallationStart:
+		v := StateStoreProviderInstallationStartMessage{}
+		return v, d.Decode(&v)
+	case ProviderQueryUsePreviousVersion:
+		v := ProviderQueryUsePreviousVersionMessage{}
+		return v, d.Decode(&v)
+	case ProviderQueryUsePreviousConstraints:
+		v := ProviderQueryUsePreviousConstraintsMessage{}
+		return v, d.Decode(&v)
+	case ProviderQueryUseLatest:
+		v := ProviderQueryUseLatestMessage{}
+		return v, d.Decode(&v)
+	case ProviderVersionAlreadyInstalled:
+		v := ProviderVersionAlreadyInstalledMessage{}
+		return v, d.Decode(&v)
+	case ProviderVersionFoundInCacheDir:
+		v := ProviderVersionFoundInCacheDirMessage{}
+		return v, d.Decode(&v)
+	case ProviderVersionInstallationStart:
+		v := ProviderVersionInstallationStartMessage{}
+		return v, d.Decode(&v)
+	case ProviderVersionInstallationComplete:
+		v := ProviderVersionInstallationCompleteMessage{}
+		return v, d.Decode(&v)
+	case BuiltInProviderAvailable:
+		v := BuiltInProviderAvailableMessage{}
+		return v, d.Decode(&v)
+	case ThirdPartyProvidersInstalled:
+		v := ThirdPartyProvidersInstalledMessage{}
+		return v, d.Decode(&v)
+	// dependency lock file
+	case ProviderLockfileCreated:
+		v := ProviderLockfileCreatedMessage{}
+		return v, d.Decode(&v)
+	case ProviderLockfileUpdated:
+		v := ProviderLockfileUpdatedMessage{}
+		return v, d.Decode(&v)
+	// provider trust
+	case ProviderInteractiveApproval:
+		v := ProviderInteractiveApprovalMessage{}
+		return v, d.Decode(&v)
+	case ProviderInteractiveRejection:
+		v := ProviderInteractiveRejectionMessage{}
+		return v, d.Decode(&v)
+	case ProviderAutomaticApproval:
+		v := ProviderAutomaticApprovalMessage{}
+		return v, d.Decode(&v)
+	// state migration
+	case LogMigrationStart:
+		v := MigrationStartMessage{}
+		return v, d.Decode(&v)
+	case LogMigrationComplete:
+		v := MigrationCompleteMessage{}
+		return v, d.Decode(&v)
+	case LogMigrationErrored:
+		v := MigrationErroredMessage{}
+		return v, d.Decode(&v)
+	case LogMigrationFinalized:
+		v := MigrationFinalizedMessage{}
+		return v, d.Decode(&v)
+	case LogMigrationSourceInitializationStart:
+		v := MigrationSourceInitializationStartMessage{}
+		return v, d.Decode(&v)
+	case LogMigrationSourceInitializationComplete:
+		v := MigrationSourceInitializationCompleteMessage{}
+		return v, d.Decode(&v)
+	case LogMigrationDestinationInitializationStart:
+		v := MigrationDestinationInitializationStartMessage{}
+		return v, d.Decode(&v)
+	case LogMigrationDestinationInitializationComplete:
+		v := MigrationDestinationInitializationCompleteMessage{}
+		return v, d.Decode(&v)
 	}
 
 	v := UnknownLogMessage{}