diff --git a/terraform/Makefile b/terraform/Makefile index f604802b5..f9940f214 100644 --- a/terraform/Makefile +++ b/terraform/Makefile @@ -79,11 +79,19 @@ endif --terraform_out=config=protoc-gen-terraform-devicetrust.yaml:./tfschema \ teleport/legacy/types/device.proto - @go run ./gen/main.go + @protoc \ + -I$(API_MOD_PATH)/proto \ + -I$(PROTOBUF_MOD_PATH) \ + --plugin=$(GENTERRAFORMPATH)/protoc-gen-terraform \ + --terraform_out=config=protoc-gen-terraform-accesslist.yaml:./tfschema \ + teleport/accesslist/v1/accesslist.proto + mv ./tfschema/github.com/gravitational/teleport/api/types/types_terraform.go ./tfschema/ mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1/loginrule_terraform.go ./tfschema/loginrule/v1/ + mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1/accesslist_terraform.go ./tfschema/accesslist/v1/ mv ./tfschema/github.com/gravitational/teleport/api/types/device_terraform.go ./tfschema/devicetrust/v1/ rm -r ./tfschema/github.com/ + @go run ./gen/main.go @go run ./gen/main.go docs .PHONY: release diff --git a/terraform/example/access_list.tf.example b/terraform/example/access_list.tf.example new file mode 100644 index 000000000..0fc91a3ab --- /dev/null +++ b/terraform/example/access_list.tf.example @@ -0,0 +1,36 @@ +resource "teleport_access_list" "crane-operation" { + header = { + metadata = { + name = "crane-operation" + labels = { + example = "yes" + } + } + } + spec = { + description = "Used to grant access to the crane." + owners = [ + { + name = "gru" + description = "The supervillain." + } + ] + membership_requires = { + roles = ["minion"] + } + ownership_requires = { + roles = ["supervillain"] + } + grants = { + roles = ["crane-operator"] + traits = [{ + key = "allowed-machines" + values = ["crane", "forklift"] + }] + } + title = "Crane operation" + audit = { + frequency = "3600h" // 150 days + } + } +} diff --git a/terraform/example/terraform.yaml b/terraform/example/terraform.yaml index 4aec1933e..608d274bc 100644 --- a/terraform/example/terraform.yaml +++ b/terraform/example/terraform.yaml @@ -28,6 +28,7 @@ spec: - login_rule - device - okta_import_rule + - access_list verbs: ['list','create','read','update','delete'] version: v6 --- diff --git a/terraform/gen/main.go b/terraform/gen/main.go index 887e7a35c..291c9aac1 100644 --- a/terraform/gen/main.go +++ b/terraform/gen/main.go @@ -35,6 +35,7 @@ import ( "github.com/gravitational/teleport-plugins/terraform/provider" "github.com/gravitational/teleport-plugins/terraform/tfschema" + accesslistSchema "github.com/gravitational/teleport-plugins/terraform/tfschema/accesslist/v1" devicetrustSchema "github.com/gravitational/teleport-plugins/terraform/tfschema/devicetrust/v1" loginruleSchema "github.com/gravitational/teleport-plugins/terraform/tfschema/loginrule/v1" ) @@ -93,6 +94,8 @@ type payload struct { // IsPlainStruct states whether the resource type used by the API methods // for this resource is a plain struct, rather than an interface. IsPlainStruct bool + // HasCheckAndSetDefaults indicates whether the resource type has the CheckAndSetDefaults method + HasCheckAndSetDefaults bool // ExtraImports contains a list of imports that are being used. ExtraImports []string // TerraformResourceType represents the resource type in Terraform code. @@ -101,6 +104,8 @@ type payload struct { TerraformResourceType string // WithNonce is used to force upsert behavior for nonce protected values. WithNonce bool + // ConvertPackagePath is the path of the package doing the conversion between protobuf and the go types. + ConvertPackagePath string } func (p *payload) CheckAndSetDefaults() error { @@ -132,195 +137,208 @@ const ( var ( app = payload{ - Name: "App", - TypeName: "AppV3", - VarName: "app", - IfaceName: "Application", - GetMethod: "GetApp", - CreateMethod: "CreateApp", - UpdateMethod: "UpdateApp", - DeleteMethod: "DeleteApp", - ID: `app.Metadata.Name`, - Kind: "app", - HasStaticID: false, - TerraformResourceType: "teleport_app", + Name: "App", + TypeName: "AppV3", + VarName: "app", + IfaceName: "Application", + GetMethod: "GetApp", + CreateMethod: "CreateApp", + UpdateMethod: "UpdateApp", + DeleteMethod: "DeleteApp", + ID: `app.Metadata.Name`, + Kind: "app", + HasStaticID: false, + TerraformResourceType: "teleport_app", + HasCheckAndSetDefaults: true, } authPreference = payload{ - Name: "AuthPreference", - TypeName: "AuthPreferenceV2", - VarName: "authPreference", - GetMethod: "GetAuthPreference", - CreateMethod: "SetAuthPreference", - UpdateMethod: "SetAuthPreference", - DeleteMethod: "ResetAuthPreference", - ID: `"auth_preference"`, - Kind: "cluster_auth_preference", - HasStaticID: false, - TerraformResourceType: "teleport_auth_preference", + Name: "AuthPreference", + TypeName: "AuthPreferenceV2", + VarName: "authPreference", + GetMethod: "GetAuthPreference", + CreateMethod: "SetAuthPreference", + UpdateMethod: "SetAuthPreference", + DeleteMethod: "ResetAuthPreference", + ID: `"auth_preference"`, + Kind: "cluster_auth_preference", + HasStaticID: false, + TerraformResourceType: "teleport_auth_preference", + HasCheckAndSetDefaults: true, } clusterMaintenance = payload{ - Name: "ClusterMaintenanceConfig", - TypeName: "ClusterMaintenanceConfigV1", - VarName: "clusterMaintenanceConfig", - GetMethod: "GetClusterMaintenanceConfig", - CreateMethod: "UpdateClusterMaintenanceConfig", - UpdateMethod: "UpdateClusterMaintenanceConfig", - DeleteMethod: "DeleteClusterMaintenanceConfig", - ID: `"cluster_maintenance_config"`, - Kind: "cluster_maintenance_config", - HasStaticID: true, - TerraformResourceType: "teleport_cluster_maintenance_config", - WithNonce: true, + Name: "ClusterMaintenanceConfig", + TypeName: "ClusterMaintenanceConfigV1", + VarName: "clusterMaintenanceConfig", + GetMethod: "GetClusterMaintenanceConfig", + CreateMethod: "UpdateClusterMaintenanceConfig", + UpdateMethod: "UpdateClusterMaintenanceConfig", + DeleteMethod: "DeleteClusterMaintenanceConfig", + ID: `"cluster_maintenance_config"`, + Kind: "cluster_maintenance_config", + HasStaticID: true, + TerraformResourceType: "teleport_cluster_maintenance_config", + WithNonce: true, + HasCheckAndSetDefaults: true, } clusterNetworking = payload{ - Name: "ClusterNetworkingConfig", - TypeName: "ClusterNetworkingConfigV2", - VarName: "clusterNetworkingConfig", - GetMethod: "GetClusterNetworkingConfig", - CreateMethod: "SetClusterNetworkingConfig", - UpdateMethod: "SetClusterNetworkingConfig", - DeleteMethod: "ResetClusterNetworkingConfig", - ID: `"cluster_networking_config"`, - Kind: "cluster_networking_config", - HasStaticID: false, - TerraformResourceType: "teleport_cluster_networking_config", + Name: "ClusterNetworkingConfig", + TypeName: "ClusterNetworkingConfigV2", + VarName: "clusterNetworkingConfig", + GetMethod: "GetClusterNetworkingConfig", + CreateMethod: "SetClusterNetworkingConfig", + UpdateMethod: "SetClusterNetworkingConfig", + DeleteMethod: "ResetClusterNetworkingConfig", + ID: `"cluster_networking_config"`, + Kind: "cluster_networking_config", + HasStaticID: false, + TerraformResourceType: "teleport_cluster_networking_config", + HasCheckAndSetDefaults: true, } database = payload{ - Name: "Database", - TypeName: "DatabaseV3", - VarName: "database", - GetMethod: "GetDatabase", - CreateMethod: "CreateDatabase", - UpdateMethod: "UpdateDatabase", - DeleteMethod: "DeleteDatabase", - ID: `database.Metadata.Name`, - Kind: "db", - HasStaticID: false, - TerraformResourceType: "teleport_database", + Name: "Database", + TypeName: "DatabaseV3", + VarName: "database", + GetMethod: "GetDatabase", + CreateMethod: "CreateDatabase", + UpdateMethod: "UpdateDatabase", + DeleteMethod: "DeleteDatabase", + ID: `database.Metadata.Name`, + Kind: "db", + HasStaticID: false, + TerraformResourceType: "teleport_database", + HasCheckAndSetDefaults: true, } githubConnector = payload{ - Name: "GithubConnector", - TypeName: "GithubConnectorV3", - VarName: "githubConnector", - GetMethod: "GetGithubConnector", - CreateMethod: "UpsertGithubConnector", - UpdateMethod: "UpsertGithubConnector", - DeleteMethod: "DeleteGithubConnector", - WithSecrets: "true", - ID: "githubConnector.Metadata.Name", - Kind: "github", - HasStaticID: true, - TerraformResourceType: "teleport_github_connector", + Name: "GithubConnector", + TypeName: "GithubConnectorV3", + VarName: "githubConnector", + GetMethod: "GetGithubConnector", + CreateMethod: "UpsertGithubConnector", + UpdateMethod: "UpsertGithubConnector", + DeleteMethod: "DeleteGithubConnector", + WithSecrets: "true", + ID: "githubConnector.Metadata.Name", + Kind: "github", + HasStaticID: true, + TerraformResourceType: "teleport_github_connector", + HasCheckAndSetDefaults: true, } oidcConnector = payload{ - Name: "OIDCConnector", - TypeName: "OIDCConnectorV3", - VarName: "oidcConnector", - GetMethod: "GetOIDCConnector", - CreateMethod: "UpsertOIDCConnector", - UpdateMethod: "UpsertOIDCConnector", - DeleteMethod: "DeleteOIDCConnector", - WithSecrets: "true", - ID: "oidcConnector.Metadata.Name", - Kind: "oidc", - HasStaticID: true, - TerraformResourceType: "teleport_oidc_connector", + Name: "OIDCConnector", + TypeName: "OIDCConnectorV3", + VarName: "oidcConnector", + GetMethod: "GetOIDCConnector", + CreateMethod: "UpsertOIDCConnector", + UpdateMethod: "UpsertOIDCConnector", + DeleteMethod: "DeleteOIDCConnector", + WithSecrets: "true", + ID: "oidcConnector.Metadata.Name", + Kind: "oidc", + HasStaticID: true, + TerraformResourceType: "teleport_oidc_connector", + HasCheckAndSetDefaults: true, } samlConnector = payload{ - Name: "SAMLConnector", - TypeName: "SAMLConnectorV2", - VarName: "samlConnector", - GetMethod: "GetSAMLConnector", - CreateMethod: "UpsertSAMLConnector", - UpdateMethod: "UpsertSAMLConnector", - DeleteMethod: "DeleteSAMLConnector", - WithSecrets: "true", - ID: "samlConnector.Metadata.Name", - Kind: "saml", - HasStaticID: true, - TerraformResourceType: "teleport_saml_connector", + Name: "SAMLConnector", + TypeName: "SAMLConnectorV2", + VarName: "samlConnector", + GetMethod: "GetSAMLConnector", + CreateMethod: "UpsertSAMLConnector", + UpdateMethod: "UpsertSAMLConnector", + DeleteMethod: "DeleteSAMLConnector", + WithSecrets: "true", + ID: "samlConnector.Metadata.Name", + Kind: "saml", + HasStaticID: true, + TerraformResourceType: "teleport_saml_connector", + HasCheckAndSetDefaults: true, } provisionToken = payload{ - Name: "ProvisionToken", - TypeName: "ProvisionTokenV2", - VarName: "provisionToken", - GetMethod: "GetToken", - CreateMethod: "UpsertToken", - UpdateMethod: "UpsertToken", - DeleteMethod: "DeleteToken", - ID: "strconv.FormatInt(provisionToken.Metadata.ID, 10)", // must be a string - RandomMetadataName: true, - Kind: "token", - HasStaticID: false, - ExtraImports: []string{"strconv"}, - TerraformResourceType: "teleport_provision_token", + Name: "ProvisionToken", + TypeName: "ProvisionTokenV2", + VarName: "provisionToken", + GetMethod: "GetToken", + CreateMethod: "UpsertToken", + UpdateMethod: "UpsertToken", + DeleteMethod: "DeleteToken", + ID: "strconv.FormatInt(provisionToken.Metadata.ID, 10)", // must be a string + RandomMetadataName: true, + Kind: "token", + HasStaticID: false, + ExtraImports: []string{"strconv"}, + TerraformResourceType: "teleport_provision_token", + HasCheckAndSetDefaults: true, } role = payload{ - Name: "Role", - TypeName: "RoleV6", - VarName: "role", - GetMethod: "GetRole", - CreateMethod: "UpsertRole", - UpdateMethod: "UpsertRole", - DeleteMethod: "DeleteRole", - ID: "role.Metadata.Name", - Kind: "role", - HasStaticID: false, - TerraformResourceType: "teleport_role", + Name: "Role", + TypeName: "RoleV6", + VarName: "role", + GetMethod: "GetRole", + CreateMethod: "UpsertRole", + UpdateMethod: "UpsertRole", + DeleteMethod: "DeleteRole", + ID: "role.Metadata.Name", + Kind: "role", + HasStaticID: false, + TerraformResourceType: "teleport_role", + HasCheckAndSetDefaults: true, } sessionRecording = payload{ - Name: "SessionRecordingConfig", - TypeName: "SessionRecordingConfigV2", - VarName: "sessionRecordingConfig", - GetMethod: "GetSessionRecordingConfig", - CreateMethod: "SetSessionRecordingConfig", - UpdateMethod: "SetSessionRecordingConfig", - DeleteMethod: "ResetSessionRecordingConfig", - ID: `"session_recording_config"`, - Kind: "session_recording_config", - HasStaticID: false, - TerraformResourceType: "teleport_session_recording_config", + Name: "SessionRecordingConfig", + TypeName: "SessionRecordingConfigV2", + VarName: "sessionRecordingConfig", + GetMethod: "GetSessionRecordingConfig", + CreateMethod: "SetSessionRecordingConfig", + UpdateMethod: "SetSessionRecordingConfig", + DeleteMethod: "ResetSessionRecordingConfig", + ID: `"session_recording_config"`, + Kind: "session_recording_config", + HasStaticID: false, + TerraformResourceType: "teleport_session_recording_config", + HasCheckAndSetDefaults: true, } trustedCluster = payload{ - Name: "TrustedCluster", - TypeName: "TrustedClusterV2", - VarName: "trustedCluster", - GetMethod: "GetTrustedCluster", - CreateMethod: "UpsertTrustedCluster", - UpdateMethod: "UpsertTrustedCluster", - DeleteMethod: "DeleteTrustedCluster", - UpsertMethodArity: 2, - ID: "trustedCluster.Metadata.Name", - Kind: "trusted_cluster", - HasStaticID: false, - TerraformResourceType: "teleport_trusted_cluster", + Name: "TrustedCluster", + TypeName: "TrustedClusterV2", + VarName: "trustedCluster", + GetMethod: "GetTrustedCluster", + CreateMethod: "UpsertTrustedCluster", + UpdateMethod: "UpsertTrustedCluster", + DeleteMethod: "DeleteTrustedCluster", + UpsertMethodArity: 2, + ID: "trustedCluster.Metadata.Name", + Kind: "trusted_cluster", + HasStaticID: false, + TerraformResourceType: "teleport_trusted_cluster", + HasCheckAndSetDefaults: true, } user = payload{ - Name: "User", - TypeName: "UserV2", - VarName: "user", - GetMethod: "GetUser", - CreateMethod: "CreateUser", - UpdateMethod: "UpdateUser", - DeleteMethod: "DeleteUser", - WithSecrets: "false", - GetWithoutContext: true, - ID: "user.Metadata.Name", - Kind: "user", - HasStaticID: false, - TerraformResourceType: "teleport_user", + Name: "User", + TypeName: "UserV2", + VarName: "user", + GetMethod: "GetUser", + CreateMethod: "CreateUser", + UpdateMethod: "UpdateUser", + DeleteMethod: "DeleteUser", + WithSecrets: "false", + GetWithoutContext: true, + ID: "user.Metadata.Name", + Kind: "user", + HasStaticID: false, + TerraformResourceType: "teleport_user", + HasCheckAndSetDefaults: true, } loginRule = payload{ @@ -362,19 +380,41 @@ var ( } oktaImportRule = payload{ - Name: "OktaImportRule", - TypeName: "OktaImportRuleV1", - VarName: "oktaImportRule", - IfaceName: "OktaImportRule", - GetMethod: "OktaClient().GetOktaImportRule", - CreateMethod: "OktaClient().CreateOktaImportRule", - UpdateMethod: "OktaClient().UpdateOktaImportRule", - DeleteMethod: "OktaClient().DeleteOktaImportRule", - UpsertMethodArity: 2, - ID: "oktaImportRule.Metadata.Name", - Kind: "okta_import_rule", - HasStaticID: false, - TerraformResourceType: "teleport_okta_import_rule", + Name: "OktaImportRule", + TypeName: "OktaImportRuleV1", + VarName: "oktaImportRule", + IfaceName: "OktaImportRule", + GetMethod: "OktaClient().GetOktaImportRule", + CreateMethod: "OktaClient().CreateOktaImportRule", + UpdateMethod: "OktaClient().UpdateOktaImportRule", + DeleteMethod: "OktaClient().DeleteOktaImportRule", + UpsertMethodArity: 2, + ID: "oktaImportRule.Metadata.Name", + Kind: "okta_import_rule", + HasStaticID: false, + TerraformResourceType: "teleport_okta_import_rule", + HasCheckAndSetDefaults: true, + } + + accessList = payload{ + Name: "AccessList", + TypeName: "AccessList", + VarName: "accessList", + GetMethod: "AccessListClient().GetAccessList", + CreateMethod: "AccessListClient().UpsertAccessList", + UpsertMethodArity: 2, + UpdateMethod: "AccessListClient().UpsertAccessList", + DeleteMethod: "AccessListClient().DeleteAccessList", + ID: "accessList.Header.Metadata.Name", + Kind: "access_list", + HasStaticID: false, + SchemaPackage: "schemav1", + SchemaPackagePath: "github.com/gravitational/teleport-plugins/terraform/tfschema/accesslist/v1", + ProtoPackage: "accesslist", + ProtoPackagePath: "github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1", + TerraformResourceType: "teleport_access_list", + ConvertPackagePath: "github.com/gravitational/teleport/api/types/accesslist/convert/v1", + HasCheckAndSetDefaults: true, } ) @@ -419,6 +459,8 @@ func genTFSchema() { generateDataSource(deviceTrust, pluralDataSource) generateResource(oktaImportRule, pluralResource) generateDataSource(oktaImportRule, pluralDataSource) + generateResource(accessList, pluralResource) + generateDataSource(accessList, pluralDataSource) } func generateResource(p payload, tpl string) { @@ -455,6 +497,7 @@ func generate(p payload, tpl, outFile string) { // Create Docs Markdown var ( mapResourceSchema = map[string]func(context.Context) (tfsdk.Schema, diag.Diagnostics){ + "access_list": accesslistSchema.GenSchemaAccessList, "app": tfschema.GenSchemaAppV3, "auth_preference": tfschema.GenSchemaAuthPreferenceV2, "bot": provider.GenSchemaBot, diff --git a/terraform/gen/plural_data_source.go.tpl b/terraform/gen/plural_data_source.go.tpl index 00e421365..8ce988456 100644 --- a/terraform/gen/plural_data_source.go.tpl +++ b/terraform/gen/plural_data_source.go.tpl @@ -27,7 +27,11 @@ import ( {{.SchemaPackage}} "{{.SchemaPackagePath}}" {{if not .IsPlainStruct -}} + {{- if .ConvertPackagePath}} + convert "{{.ConvertPackagePath}}" + {{ else }} {{.ProtoPackage}} "{{.ProtoPackagePath}}" + {{- end}} {{end -}} "github.com/gravitational/trace" ) @@ -55,7 +59,11 @@ func (r dataSourceTeleport{{.Name}}Type) NewDataSource(_ context.Context, p tfsd // Read reads teleport {{.Name}} func (r dataSourceTeleport{{.Name}}) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { var id types.String + {{- if .ConvertPackagePath}} + diags := req.Config.GetAttribute(ctx, path.Root("header").AtName("metadata").AtName("name"), &id) + {{- else }} diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id) + {{- end}} resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return @@ -70,9 +78,11 @@ func (r dataSourceTeleport{{.Name}}) Read(ctx context.Context, req tfsdk.ReadDat var state types.Object {{if .IsPlainStruct -}} {{.VarName}} := {{.VarName}}I - {{else -}} + {{else if .ConvertPackagePath -}} + {{.VarName}} := convert.ToProto({{.VarName}}I) + {{else}} {{.VarName}} := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}}) - {{end -}} + {{- end}} diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { diff --git a/terraform/gen/plural_resource.go.tpl b/terraform/gen/plural_resource.go.tpl index 718875b6a..6ae017591 100644 --- a/terraform/gen/plural_resource.go.tpl +++ b/terraform/gen/plural_resource.go.tpl @@ -38,6 +38,9 @@ import ( {{.ProtoPackage}} "{{.ProtoPackagePath}}" {{.SchemaPackage}} "{{.SchemaPackagePath}}" +{{- if .ConvertPackagePath}} + convert "{{.ConvertPackagePath}}" +{{- end}} "github.com/gravitational/teleport/integrations/lib/backoff" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" @@ -65,6 +68,7 @@ func (r resourceTeleport{{.Name}}Type) NewResource(_ context.Context, p tfsdk.Pr // Create creates the {{.Name}} func (r resourceTeleport{{.Name}}) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { + var err error if !r.p.IsConfigured(resp.Diagnostics) { return } @@ -104,13 +108,22 @@ func (r resourceTeleport{{.Name}}) Create(ctx context.Context, req tfsdk.CreateR {{.VarName}}.Version = "{{.DefaultVersion}}" } {{- end}} +{{- if .ConvertPackagePath}} + {{.VarName}}Resource, err := convert.FromProto({{.VarName}}) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Errorf("Can not convert %T to {{.TypeName}}: %s", {{.VarName}}Resource, err), "{{.Kind}}")) + return + } +{{- else }} + {{.VarName}}Resource := {{.VarName}} +{{end}} + id := {{.VarName}}Resource.Metadata.Name - _, err := r.p.Client.{{.GetMethod}}({{if not .GetWithoutContext}}ctx, {{end}}{{.VarName}}.Metadata.Name{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}}) + _, err = r.p.Client.{{.GetMethod}}({{if not .GetWithoutContext}}ctx, {{end}}id{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}}) if !trace.IsNotFound(err) { if err == nil { - n := {{.VarName}}.Metadata.Name existErr := fmt.Sprintf("{{.Name}} exists in Teleport. Either remove it (tctl rm {{.Kind}}/%v)"+ - " or import it to the existing state (terraform import {{.TerraformResourceType}}.%v %v)", n, n, n) + " or import it to the existing state (terraform import {{.TerraformResourceType}}.%v %v)", id, id, id) resp.Diagnostics.Append(diagFromErr("{{.Name}} exists in Teleport", trace.Errorf(existErr))) return @@ -120,28 +133,29 @@ func (r resourceTeleport{{.Name}}) Create(ctx context.Context, req tfsdk.CreateR return } - {{if not .IsPlainStruct -}} - err = {{.VarName}}.CheckAndSetDefaults() + {{if .HasCheckAndSetDefaults -}} + err = {{.VarName}}Resource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting {{.Name}} defaults", trace.Wrap(err), "{{.Kind}}")) return } {{- end}} - {{if eq .UpsertMethodArity 2}}_, {{end}}err = r.p.Client.{{.CreateMethod}}(ctx, {{.VarName}}) + {{if eq .UpsertMethodArity 2}}_, {{end}}err = r.p.Client.{{.CreateMethod}}(ctx, {{.VarName}}Resource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating {{.Name}}", trace.Wrap(err), "{{.Kind}}")) return } - - id := {{.VarName}}.Metadata.Name - {{if .IsPlainStruct -}} + {{- if .IsPlainStruct }} + var {{.VarName}}I *{{.ProtoPackage}}.{{.Name}} + {{- else }} + {{if .ConvertPackagePath -}} + var {{.VarName}}I = {{.VarName}}Resource + {{- else }} // Not really an inferface, just using the same name for easier templating. - var {{.VarName}}I *{{.ProtoPackage}}.{{.Name}} - {{else -}} var {{.VarName}}I {{.ProtoPackage}}.{{ if ne .IfaceName ""}}{{.IfaceName}}{{else}}{{.Name}}{{end}} - {{- end}} - + {{- end}} + {{- end }} tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { @@ -167,16 +181,22 @@ func (r resourceTeleport{{.Name}}) Create(ctx context.Context, req tfsdk.CreateR return } - {{if .IsPlainStruct -}} - {{.VarName}} = {{.VarName}}I + {{if or .IsPlainStruct .ConvertPackagePath -}} + {{.VarName}}Resource = {{.VarName}}I {{else -}} - {{.VarName}}, ok := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}}) + {{.VarName}}Resource, ok := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}}) if !ok { resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Errorf("Can not convert %T to {{.TypeName}}", {{.VarName}}I), "{{.Kind}}")) return } {{- end}} + {{- if .ConvertPackagePath}} + {{.VarName}} = convert.ToProto({{.VarName}}Resource) + {{else}} + {{.VarName}} = {{.VarName}}Resource + {{- end }} + diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -202,7 +222,11 @@ func (r resourceTeleport{{.Name}}) Read(ctx context.Context, req tfsdk.ReadResou } var id types.String + {{- if .ConvertPackagePath}} + diags = req.State.GetAttribute(ctx, path.Root("header").AtName("metadata").AtName("name"), &id) + {{- else }} diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id) + {{- end}} resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return @@ -218,10 +242,11 @@ func (r resourceTeleport{{.Name}}) Read(ctx context.Context, req tfsdk.ReadResou resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}")) return } - {{if .IsPlainStruct -}} {{.VarName}} := {{.VarName}}I - {{else -}} + {{else if .ConvertPackagePath -}} + {{.VarName}} := convert.ToProto({{.VarName}}I) + {{ else }} {{.VarName}} := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}}) {{end -}} diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &state) @@ -258,15 +283,23 @@ func (r resourceTeleport{{.Name}}) Update(ctx context.Context, req tfsdk.UpdateR return } - name := {{.VarName}}.Metadata.Name - - {{if not .IsPlainStruct -}} - err := {{.VarName}}.CheckAndSetDefaults() +{{- if .ConvertPackagePath}} + {{.VarName}}Resource, err := convert.FromProto({{.VarName}}) if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Errorf("Can not convert %T to {{.TypeName}}: %s", {{.VarName}}Resource, err), "{{.Kind}}")) + return + } +{{- else }} + {{.VarName}}Resource := {{.VarName}} +{{end}} + + {{if .HasCheckAndSetDefaults -}} + if err := {{.VarName}}Resource.CheckAndSetDefaults(); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating {{.Name}}", err, "{{.Kind}}")) return } {{- end}} + name := {{.VarName}}Resource.Metadata.Name {{.VarName}}Before, err := r.p.Client.{{.GetMethod}}({{if not .GetWithoutContext}}ctx, {{end}}name{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}}) if err != nil { @@ -274,38 +307,35 @@ func (r resourceTeleport{{.Name}}) Update(ctx context.Context, req tfsdk.UpdateR return } - {{if eq .UpsertMethodArity 2}}_, {{end}}err = r.p.Client.{{.UpdateMethod}}(ctx, {{.VarName}}) + {{if eq .UpsertMethodArity 2}}_, {{end}}err = r.p.Client.{{.UpdateMethod}}(ctx, {{.VarName}}Resource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating {{.Name}}", err, "{{.Kind}}")) return } - {{if not .IsPlainStruct -}} + {{- if .IsPlainStruct }} + var {{.VarName}}I *{{.ProtoPackage}}.{{.Name}} + {{- else }} + {{if .ConvertPackagePath -}} + var {{.VarName}}I = {{.VarName}}Resource + {{- else }} + // Not really an inferface, just using the same name for easier templating. var {{.VarName}}I {{.ProtoPackage}}.{{ if ne .IfaceName ""}}{{.IfaceName}}{{else}}{{.Name}}{{end}} - {{- end}} + {{- end}} + {{- end }} tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { tries = tries + 1 - {{if .IsPlainStruct -}} - {{.VarName}}, err = r.p.Client.{{.GetMethod}}({{if not .GetWithoutContext}}ctx, {{end}}name{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}}) - {{else -}} {{.VarName}}I, err = r.p.Client.{{.GetMethod}}({{if not .GetWithoutContext}}ctx, {{end}}name{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}}) - {{end -}} if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", err, "{{.Kind}}")) return } - {{if .IsPlainStruct -}} - if {{.VarName}}Before.GetMetadata().ID != {{.VarName}}.GetMetadata().ID || {{.HasStaticID}} { - break - } - {{else -}} if {{.VarName}}Before.GetMetadata().ID != {{.VarName}}I.GetMetadata().ID || {{.HasStaticID}} { break } - {{- end}} if err := backoff.Do(ctx); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}")) @@ -318,9 +348,15 @@ func (r resourceTeleport{{.Name}}) Update(ctx context.Context, req tfsdk.UpdateR } } - {{if not .IsPlainStruct -}} - {{.VarName}} = {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}}) - {{end -}} + {{if or .IsPlainStruct .ConvertPackagePath -}} + {{.VarName}}Resource = {{.VarName}}I + {{else -}} + {{.VarName}}Resource, ok := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}}) + if !ok { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Errorf("Can not convert %T to {{.TypeName}}", {{.VarName}}I), "{{.Kind}}")) + return + } + {{- end}} diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -337,7 +373,11 @@ func (r resourceTeleport{{.Name}}) Update(ctx context.Context, req tfsdk.UpdateR // Delete deletes Teleport {{.Name}} func (r resourceTeleport{{.Name}}) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) { var id types.String + {{- if .ConvertPackagePath}} + diags := req.State.GetAttribute(ctx, path.Root("header").AtName("metadata").AtName("name"), &id) + {{- else }} diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id) + {{- end}} resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return @@ -354,14 +394,18 @@ func (r resourceTeleport{{.Name}}) Delete(ctx context.Context, req tfsdk.DeleteR // ImportState imports {{.Name}} state func (r resourceTeleport{{.Name}}) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - {{.VarName}}{{if not .IsPlainStruct}}I{{end}}, err := r.p.Client.{{.GetMethod}}({{if not .GetWithoutContext}}ctx, {{end}}req.ID{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}}) + {{.VarName}}, err := r.p.Client.{{.GetMethod}}({{if not .GetWithoutContext}}ctx, {{end}}req.ID{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}}) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}")) return } - {{if not .IsPlainStruct -}} - {{.VarName}} := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}}) + {{if .IsPlainStruct -}} + {{.VarName}}Resource := {{.VarName}} + {{else if .ConvertPackagePath -}} + {{.VarName}}Resource := convert.ToProto({{.VarName}}) + {{else}} + {{.VarName}}Resource := {{.VarName}}.(*{{.ProtoPackage}}.{{.TypeName}}) {{- end}} var state types.Object @@ -372,13 +416,19 @@ func (r resourceTeleport{{.Name}}) ImportState(ctx context.Context, req tfsdk.Im return } - diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &state) + diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}Resource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } - state.Attrs["id"] = types.String{Value: {{.VarName}}.Metadata.Name} +{{- if or .IsPlainStruct .ConvertPackagePath}} + id := {{.VarName}}.Metadata.Name +{{- else }} + id := {{.VarName}}Resource.GetName() +{{- end}} + + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/protoc-gen-terraform-accesslist.yaml b/terraform/protoc-gen-terraform-accesslist.yaml new file mode 100644 index 000000000..bb907536e --- /dev/null +++ b/terraform/protoc-gen-terraform-accesslist.yaml @@ -0,0 +1,75 @@ +--- +target_package_name: "v1" +default_package_name: "github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1" +duration_custom_type: Duration +use_state_for_unknown_by_default: true + +# Top-level type names to export +types: + - "AccessList" + +# These import paths were not being automatically picked up by +# protoc-gen-terraform without these overrides +import_path_overrides: + "types": "github.com/gravitational/teleport/api/types" + "wrappers": "github.com/gravitational/teleport/api/types/wrappers" + "durationpb": "google.golang.org/protobuf/types/known/durationpb" + "timestamppb": "google.golang.org/protobuf/types/known/timestamppb" + "v1": "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + "v11": "github.com/gravitational/teleport/api/gen/proto/go/teleport/trait/v1" + github_com_gravitational_teleport_plugins_terraform_tfschema: "github.com/gravitational/teleport-plugins/terraform/tfschema" + +# id field is required for integration tests. It is not used by provider. +# We have to add it manually (might be removed in the future versions). +injected_fields: + AccessList: + - + name: id + type: github.com/hashicorp/terraform-plugin-framework/types.StringType + computed: true + plan_modifiers: + - "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()" + +# These fields will be excluded +exclude_fields: + # Metadata (we id resources by name on our side) + - "AccessList.header.metadata.id" + # Read only field + - "AccessList.spec.owners.ineligible_status" + +# These fields will be marked as Computed: true +computed_fields: + # Metadata + - "AccessList.header.metadata.namespace" + - "AccessList.spec.audit.next_audit_date" + +# These fields will be marked as Required: true +required_fields: + - "Metadata.name" + - "AccessList.spec.owners" + - "AccessList.spec.grants" + - "AccessList.spec.membership_requires" + - "AccessList.spec.ownership_requires" + +plan_modifiers: + # Force to recreate resource if it's name changes + Metadata.name: + - "github.com/hashicorp/terraform-plugin-framework/tfsdk.RequiresReplace()" + +# This must be defined for the generator to be happy, but in reality all time +# fields are overridden (because the protobuf timestamps contain locks and the +# linter gets mad if we use raw structs instead of pointers). +time_type: + type: "PlaceholderType" +duration_type: + type: "PlaceholderType" + +validators: + # Expires must be in the future + Metadata.expires: + - github_com_gravitational_teleport_plugins_terraform_tfschema.MustTimeBeInFuture() + +custom_types: + "AccessList.header.metadata.expires": Timestamp + "AccessList.spec.audit.next_audit_date": Timestamp + "AccessList.spec.audit.frequency": Duration diff --git a/terraform/provider/data_source_teleport_access_list.go b/terraform/provider/data_source_teleport_access_list.go new file mode 100755 index 000000000..0cc6aca02 --- /dev/null +++ b/terraform/provider/data_source_teleport_access_list.go @@ -0,0 +1,84 @@ +// Code generated by _gen/main.go DO NOT EDIT +/* +Copyright 2015-2022 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/path" + + schemav1 "github.com/gravitational/teleport-plugins/terraform/tfschema/accesslist/v1" + + convert "github.com/gravitational/teleport/api/types/accesslist/convert/v1" + + "github.com/gravitational/trace" +) + +// dataSourceTeleportAccessListType is the data source metadata type +type dataSourceTeleportAccessListType struct{} + +// dataSourceTeleportAccessList is the resource +type dataSourceTeleportAccessList struct { + p Provider +} + +// GetSchema returns the data source schema +func (r dataSourceTeleportAccessListType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return schemav1.GenSchemaAccessList(ctx) +} + +// NewDataSource creates the empty data source +func (r dataSourceTeleportAccessListType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { + return dataSourceTeleportAccessList{ + p: *(p.(*Provider)), + }, nil +} + +// Read reads teleport AccessList +func (r dataSourceTeleportAccessList) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { + var id types.String + diags := req.Config.GetAttribute(ctx, path.Root("header").AtName("metadata").AtName("name"), &id) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + accessListI, err := r.p.Client.AccessListClient().GetAccessList(ctx, id.Value) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Wrap(err), "access_list")) + return + } + + var state types.Object + accessList := convert.ToProto(accessListI) + + diags = schemav1.CopyAccessListToTerraform(ctx, accessList, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} diff --git a/terraform/provider/data_source_teleport_app.go b/terraform/provider/data_source_teleport_app.go index caf59c20d..976313a16 100755 --- a/terraform/provider/data_source_teleport_app.go +++ b/terraform/provider/data_source_teleport_app.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportApp) Read(ctx context.Context, req tfsdk.ReadDataSourc } var state types.Object + app := appI.(*apitypes.AppV3) diags = tfschema.CopyAppV3ToTerraform(ctx, app, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_database.go b/terraform/provider/data_source_teleport_database.go index 179b9df34..415c489aa 100755 --- a/terraform/provider/data_source_teleport_database.go +++ b/terraform/provider/data_source_teleport_database.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportDatabase) Read(ctx context.Context, req tfsdk.ReadData } var state types.Object + database := databaseI.(*apitypes.DatabaseV3) diags = tfschema.CopyDatabaseV3ToTerraform(ctx, database, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_device_trust.go b/terraform/provider/data_source_teleport_device_trust.go index bee591464..75bd0a857 100755 --- a/terraform/provider/data_source_teleport_device_trust.go +++ b/terraform/provider/data_source_teleport_device_trust.go @@ -66,6 +66,7 @@ func (r dataSourceTeleportDeviceV1) Read(ctx context.Context, req tfsdk.ReadData var state types.Object trustedDevice := trustedDeviceI + diags = tfschema.CopyDeviceV1ToTerraform(ctx, trustedDevice, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { diff --git a/terraform/provider/data_source_teleport_github_connector.go b/terraform/provider/data_source_teleport_github_connector.go index 2fb0f9c09..19bd2880a 100755 --- a/terraform/provider/data_source_teleport_github_connector.go +++ b/terraform/provider/data_source_teleport_github_connector.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportGithubConnector) Read(ctx context.Context, req tfsdk.R } var state types.Object + githubConnector := githubConnectorI.(*apitypes.GithubConnectorV3) diags = tfschema.CopyGithubConnectorV3ToTerraform(ctx, githubConnector, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_login_rule.go b/terraform/provider/data_source_teleport_login_rule.go index dacfc88ee..b2c645aed 100755 --- a/terraform/provider/data_source_teleport_login_rule.go +++ b/terraform/provider/data_source_teleport_login_rule.go @@ -66,6 +66,7 @@ func (r dataSourceTeleportLoginRule) Read(ctx context.Context, req tfsdk.ReadDat var state types.Object loginRule := loginRuleI + diags = schemav1.CopyLoginRuleToTerraform(ctx, loginRule, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { diff --git a/terraform/provider/data_source_teleport_oidc_connector.go b/terraform/provider/data_source_teleport_oidc_connector.go index 7d2ec6385..b8c864cba 100755 --- a/terraform/provider/data_source_teleport_oidc_connector.go +++ b/terraform/provider/data_source_teleport_oidc_connector.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportOIDCConnector) Read(ctx context.Context, req tfsdk.Rea } var state types.Object + oidcConnector := oidcConnectorI.(*apitypes.OIDCConnectorV3) diags = tfschema.CopyOIDCConnectorV3ToTerraform(ctx, oidcConnector, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_okta_import_rule.go b/terraform/provider/data_source_teleport_okta_import_rule.go index 84e75a039..51e21bc98 100755 --- a/terraform/provider/data_source_teleport_okta_import_rule.go +++ b/terraform/provider/data_source_teleport_okta_import_rule.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportOktaImportRule) Read(ctx context.Context, req tfsdk.Re } var state types.Object + oktaImportRule := oktaImportRuleI.(*apitypes.OktaImportRuleV1) diags = tfschema.CopyOktaImportRuleV1ToTerraform(ctx, oktaImportRule, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_provision_token.go b/terraform/provider/data_source_teleport_provision_token.go index 19a3f03fd..d2d3c84d5 100755 --- a/terraform/provider/data_source_teleport_provision_token.go +++ b/terraform/provider/data_source_teleport_provision_token.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportProvisionToken) Read(ctx context.Context, req tfsdk.Re } var state types.Object + provisionToken := provisionTokenI.(*apitypes.ProvisionTokenV2) diags = tfschema.CopyProvisionTokenV2ToTerraform(ctx, provisionToken, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_role.go b/terraform/provider/data_source_teleport_role.go index 9546422a5..e7f789555 100755 --- a/terraform/provider/data_source_teleport_role.go +++ b/terraform/provider/data_source_teleport_role.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportRole) Read(ctx context.Context, req tfsdk.ReadDataSour } var state types.Object + role := roleI.(*apitypes.RoleV6) diags = tfschema.CopyRoleV6ToTerraform(ctx, role, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_saml_connector.go b/terraform/provider/data_source_teleport_saml_connector.go index 6b5dd619d..04190864c 100755 --- a/terraform/provider/data_source_teleport_saml_connector.go +++ b/terraform/provider/data_source_teleport_saml_connector.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportSAMLConnector) Read(ctx context.Context, req tfsdk.Rea } var state types.Object + samlConnector := samlConnectorI.(*apitypes.SAMLConnectorV2) diags = tfschema.CopySAMLConnectorV2ToTerraform(ctx, samlConnector, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_trusted_cluster.go b/terraform/provider/data_source_teleport_trusted_cluster.go index 5f1a37242..5136243d5 100755 --- a/terraform/provider/data_source_teleport_trusted_cluster.go +++ b/terraform/provider/data_source_teleport_trusted_cluster.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportTrustedCluster) Read(ctx context.Context, req tfsdk.Re } var state types.Object + trustedCluster := trustedClusterI.(*apitypes.TrustedClusterV2) diags = tfschema.CopyTrustedClusterV2ToTerraform(ctx, trustedCluster, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_user.go b/terraform/provider/data_source_teleport_user.go index 2d0f82b5a..8ca3d0617 100755 --- a/terraform/provider/data_source_teleport_user.go +++ b/terraform/provider/data_source_teleport_user.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportUser) Read(ctx context.Context, req tfsdk.ReadDataSour } var state types.Object + user := userI.(*apitypes.UserV2) diags = tfschema.CopyUserV2ToTerraform(ctx, user, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/provider.go b/terraform/provider/provider.go index 740f274fc..e542803b6 100644 --- a/terraform/provider/provider.go +++ b/terraform/provider/provider.go @@ -575,6 +575,7 @@ func (p *Provider) GetResources(_ context.Context) (map[string]tfsdk.ResourceTyp "teleport_login_rule": resourceTeleportLoginRuleType{}, "teleport_trusted_device": resourceTeleportDeviceV1Type{}, "teleport_okta_import_rule": resourceTeleportOktaImportRuleType{}, + "teleport_access_list": resourceTeleportAccessListType{}, }, nil } @@ -597,5 +598,6 @@ func (p *Provider) GetDataSources(_ context.Context) (map[string]tfsdk.DataSourc "teleport_login_rule": dataSourceTeleportLoginRuleType{}, "teleport_trusted_device": dataSourceTeleportDeviceV1Type{}, "teleport_okta_import_rule": dataSourceTeleportOktaImportRuleType{}, + "teleport_access_list": dataSourceTeleportAccessListType{}, }, nil } diff --git a/terraform/provider/resource_teleport_access_list.go b/terraform/provider/resource_teleport_access_list.go new file mode 100755 index 000000000..42b037b74 --- /dev/null +++ b/terraform/provider/resource_teleport_access_list.go @@ -0,0 +1,332 @@ +// Code generated by _gen/main.go DO NOT EDIT +/* +Copyright 2015-2022 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/path" + + accesslist "github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1" + schemav1 "github.com/gravitational/teleport-plugins/terraform/tfschema/accesslist/v1" + convert "github.com/gravitational/teleport/api/types/accesslist/convert/v1" + "github.com/gravitational/teleport/integrations/lib/backoff" + "github.com/gravitational/trace" + "github.com/jonboulle/clockwork" +) + +// resourceTeleportAccessListType is the resource metadata type +type resourceTeleportAccessListType struct{} + +// resourceTeleportAccessList is the resource +type resourceTeleportAccessList struct { + p Provider +} + +// GetSchema returns the resource schema +func (r resourceTeleportAccessListType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return schemav1.GenSchemaAccessList(ctx) +} + +// NewResource creates the empty resource +func (r resourceTeleportAccessListType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) { + return resourceTeleportAccessList{ + p: *(p.(*Provider)), + }, nil +} + +// Create creates the AccessList +func (r resourceTeleportAccessList) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { + var err error + if !r.p.IsConfigured(resp.Diagnostics) { + return + } + + var plan types.Object + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + accessList := &accesslist.AccessList{} + diags = schemav1.CopyAccessListFromTerraform(ctx, plan, accessList) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + + accessListResource, err := convert.FromProto(accessList) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Errorf("Can not convert %T to AccessList: %s", accessListResource, err), "access_list")) + return + } + id := accessListResource.Metadata.Name + + _, err = r.p.Client.AccessListClient().GetAccessList(ctx, id) + if !trace.IsNotFound(err) { + if err == nil { + existErr := fmt.Sprintf("AccessList exists in Teleport. Either remove it (tctl rm access_list/%v)"+ + " or import it to the existing state (terraform import teleport_access_list.%v %v)", id, id, id) + + resp.Diagnostics.Append(diagFromErr("AccessList exists in Teleport", trace.Errorf(existErr))) + return + } + + resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Wrap(err), "access_list")) + return + } + + err = accessListResource.CheckAndSetDefaults() + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error setting AccessList defaults", trace.Wrap(err), "access_list")) + return + } + + _, err = r.p.Client.AccessListClient().UpsertAccessList(ctx, accessListResource) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error creating AccessList", trace.Wrap(err), "access_list")) + return + } + var accessListI = accessListResource + tries := 0 + backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) + for { + tries = tries + 1 + accessListI, err = r.p.Client.AccessListClient().GetAccessList(ctx, id) + if trace.IsNotFound(err) { + if bErr := backoff.Do(ctx); bErr != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Wrap(err), "access_list")) + return + } + if tries >= r.p.RetryConfig.MaxTries { + diagMessage := fmt.Sprintf("Error reading AccessList (tried %d times) - state outdated, please import resource", tries) + resp.Diagnostics.Append(diagFromWrappedErr(diagMessage, trace.Wrap(err), "access_list")) + return + } + continue + } + break + } + + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Wrap(err), "access_list")) + return + } + + accessListResource = accessListI + + accessList = convert.ToProto(accessListResource) + + + diags = schemav1.CopyAccessListToTerraform(ctx, accessList, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + plan.Attrs["id"] = types.String{Value: accessList.Header.Metadata.Name} + + diags = resp.State.Set(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Read reads teleport AccessList +func (r resourceTeleportAccessList) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + var id types.String + diags = req.State.GetAttribute(ctx, path.Root("header").AtName("metadata").AtName("name"), &id) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + accessListI, err := r.p.Client.AccessListClient().GetAccessList(ctx, id.Value) + if trace.IsNotFound(err) { + resp.State.RemoveResource(ctx) + return + } + + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Wrap(err), "access_list")) + return + } + accessList := convert.ToProto(accessListI) + diags = schemav1.CopyAccessListToTerraform(ctx, accessList, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Update updates teleport AccessList +func (r resourceTeleportAccessList) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) { + if !r.p.IsConfigured(resp.Diagnostics) { + return + } + + var plan types.Object + diags := req.Plan.Get(ctx, &plan) + + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + accessList := &accesslist.AccessList{} + diags = schemav1.CopyAccessListFromTerraform(ctx, plan, accessList) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + accessListResource, err := convert.FromProto(accessList) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Errorf("Can not convert %T to AccessList: %s", accessListResource, err), "access_list")) + return + } + + if err := accessListResource.CheckAndSetDefaults(); err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error updating AccessList", err, "access_list")) + return + } + name := accessListResource.Metadata.Name + + accessListBefore, err := r.p.Client.AccessListClient().GetAccessList(ctx, name) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", err, "access_list")) + return + } + + _, err = r.p.Client.AccessListClient().UpsertAccessList(ctx, accessListResource) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error updating AccessList", err, "access_list")) + return + } + var accessListI = accessListResource + + tries := 0 + backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) + for { + tries = tries + 1 + accessListI, err = r.p.Client.AccessListClient().GetAccessList(ctx, name) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", err, "access_list")) + return + } + if accessListBefore.GetMetadata().ID != accessListI.GetMetadata().ID || false { + break + } + + if err := backoff.Do(ctx); err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Wrap(err), "access_list")) + return + } + if tries >= r.p.RetryConfig.MaxTries { + diagMessage := fmt.Sprintf("Error reading AccessList (tried %d times) - state outdated, please import resource", tries) + resp.Diagnostics.AddError(diagMessage, "access_list") + return + } + } + + accessListResource = accessListI + + diags = schemav1.CopyAccessListToTerraform(ctx, accessList, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = resp.State.Set(ctx, plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Delete deletes Teleport AccessList +func (r resourceTeleportAccessList) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) { + var id types.String + diags := req.State.GetAttribute(ctx, path.Root("header").AtName("metadata").AtName("name"), &id) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + err := r.p.Client.AccessListClient().DeleteAccessList(ctx, id.Value) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error deleting AccessList", trace.Wrap(err), "access_list")) + return + } + + resp.State.RemoveResource(ctx) +} + +// ImportState imports AccessList state +func (r resourceTeleportAccessList) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { + accessList, err := r.p.Client.AccessListClient().GetAccessList(ctx, req.ID) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Wrap(err), "access_list")) + return + } + + accessListResource := convert.ToProto(accessList) + + + var state types.Object + + diags := resp.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = schemav1.CopyAccessListToTerraform(ctx, accessListResource, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + id := accessList.Metadata.Name + + state.Attrs["id"] = types.String{Value: id} + + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} diff --git a/terraform/provider/resource_teleport_app.go b/terraform/provider/resource_teleport_app.go index c3d7ffcd6..358c7cba9 100755 --- a/terraform/provider/resource_teleport_app.go +++ b/terraform/provider/resource_teleport_app.go @@ -55,6 +55,7 @@ func (r resourceTeleportAppType) NewResource(_ context.Context, p tfsdk.Provider // Create creates the App func (r resourceTeleportApp) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { + var err error if !r.p.IsConfigured(resp.Diagnostics) { return } @@ -74,13 +75,15 @@ func (r resourceTeleportApp) Create(ctx context.Context, req tfsdk.CreateResourc } + appResource := app - _, err := r.p.Client.GetApp(ctx, app.Metadata.Name) + id := appResource.Metadata.Name + + _, err = r.p.Client.GetApp(ctx, id) if !trace.IsNotFound(err) { if err == nil { - n := app.Metadata.Name existErr := fmt.Sprintf("App exists in Teleport. Either remove it (tctl rm app/%v)"+ - " or import it to the existing state (terraform import teleport_app.%v %v)", n, n, n) + " or import it to the existing state (terraform import teleport_app.%v %v)", id, id, id) resp.Diagnostics.Append(diagFromErr("App exists in Teleport", trace.Errorf(existErr))) return @@ -90,21 +93,20 @@ func (r resourceTeleportApp) Create(ctx context.Context, req tfsdk.CreateResourc return } - err = app.CheckAndSetDefaults() + err = appResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting App defaults", trace.Wrap(err), "app")) return } - err = r.p.Client.CreateApp(ctx, app) + err = r.p.Client.CreateApp(ctx, appResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating App", trace.Wrap(err), "app")) return } - - id := app.Metadata.Name + + // Not really an inferface, just using the same name for easier templating. var appI apitypes.Application - tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { @@ -130,11 +132,12 @@ func (r resourceTeleportApp) Create(ctx context.Context, req tfsdk.CreateResourc return } - app, ok := appI.(*apitypes.AppV3) + appResource, ok := appI.(*apitypes.AppV3) if !ok { resp.Diagnostics.Append(diagFromWrappedErr("Error reading App", trace.Errorf("Can not convert %T to AppV3", appI), "app")) return } + app = appResource diags = tfschema.CopyAppV3ToTerraform(ctx, app, &plan) resp.Diagnostics.Append(diags...) @@ -177,7 +180,7 @@ func (r resourceTeleportApp) Read(ctx context.Context, req tfsdk.ReadResourceReq resp.Diagnostics.Append(diagFromWrappedErr("Error reading App", trace.Wrap(err), "app")) return } - + app := appI.(*apitypes.AppV3) diags = tfschema.CopyAppV3ToTerraform(ctx, app, &state) resp.Diagnostics.Append(diags...) @@ -212,14 +215,14 @@ func (r resourceTeleportApp) Update(ctx context.Context, req tfsdk.UpdateResourc if resp.Diagnostics.HasError() { return } + appResource := app - name := app.Metadata.Name - err := app.CheckAndSetDefaults() - if err != nil { + if err := appResource.CheckAndSetDefaults(); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating App", err, "app")) return } + name := appResource.Metadata.Name appBefore, err := r.p.Client.GetApp(ctx, name) if err != nil { @@ -227,12 +230,13 @@ func (r resourceTeleportApp) Update(ctx context.Context, req tfsdk.UpdateResourc return } - err = r.p.Client.UpdateApp(ctx, app) + err = r.p.Client.UpdateApp(ctx, appResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating App", err, "app")) return } - + + // Not really an inferface, just using the same name for easier templating. var appI apitypes.Application tries := 0 @@ -259,7 +263,11 @@ func (r resourceTeleportApp) Update(ctx context.Context, req tfsdk.UpdateResourc } } - app = appI.(*apitypes.AppV3) + appResource, ok := appI.(*apitypes.AppV3) + if !ok { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading App", trace.Errorf("Can not convert %T to AppV3", appI), "app")) + return + } diags = tfschema.CopyAppV3ToTerraform(ctx, app, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -293,13 +301,14 @@ func (r resourceTeleportApp) Delete(ctx context.Context, req tfsdk.DeleteResourc // ImportState imports App state func (r resourceTeleportApp) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - appI, err := r.p.Client.GetApp(ctx, req.ID) + app, err := r.p.Client.GetApp(ctx, req.ID) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading App", trace.Wrap(err), "app")) return } - app := appI.(*apitypes.AppV3) + + appResource := app.(*apitypes.AppV3) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportApp) ImportState(ctx context.Context, req tfsdk.ImportRe return } - diags = tfschema.CopyAppV3ToTerraform(ctx, app, &state) + diags = tfschema.CopyAppV3ToTerraform(ctx, appResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := appResource.GetName() - state.Attrs["id"] = types.String{Value: app.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_database.go b/terraform/provider/resource_teleport_database.go index 71f13358e..82df15ef9 100755 --- a/terraform/provider/resource_teleport_database.go +++ b/terraform/provider/resource_teleport_database.go @@ -55,6 +55,7 @@ func (r resourceTeleportDatabaseType) NewResource(_ context.Context, p tfsdk.Pro // Create creates the Database func (r resourceTeleportDatabase) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { + var err error if !r.p.IsConfigured(resp.Diagnostics) { return } @@ -74,13 +75,15 @@ func (r resourceTeleportDatabase) Create(ctx context.Context, req tfsdk.CreateRe } + databaseResource := database - _, err := r.p.Client.GetDatabase(ctx, database.Metadata.Name) + id := databaseResource.Metadata.Name + + _, err = r.p.Client.GetDatabase(ctx, id) if !trace.IsNotFound(err) { if err == nil { - n := database.Metadata.Name existErr := fmt.Sprintf("Database exists in Teleport. Either remove it (tctl rm db/%v)"+ - " or import it to the existing state (terraform import teleport_database.%v %v)", n, n, n) + " or import it to the existing state (terraform import teleport_database.%v %v)", id, id, id) resp.Diagnostics.Append(diagFromErr("Database exists in Teleport", trace.Errorf(existErr))) return @@ -90,21 +93,20 @@ func (r resourceTeleportDatabase) Create(ctx context.Context, req tfsdk.CreateRe return } - err = database.CheckAndSetDefaults() + err = databaseResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting Database defaults", trace.Wrap(err), "db")) return } - err = r.p.Client.CreateDatabase(ctx, database) + err = r.p.Client.CreateDatabase(ctx, databaseResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating Database", trace.Wrap(err), "db")) return } - - id := database.Metadata.Name + + // Not really an inferface, just using the same name for easier templating. var databaseI apitypes.Database - tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { @@ -130,11 +132,12 @@ func (r resourceTeleportDatabase) Create(ctx context.Context, req tfsdk.CreateRe return } - database, ok := databaseI.(*apitypes.DatabaseV3) + databaseResource, ok := databaseI.(*apitypes.DatabaseV3) if !ok { resp.Diagnostics.Append(diagFromWrappedErr("Error reading Database", trace.Errorf("Can not convert %T to DatabaseV3", databaseI), "db")) return } + database = databaseResource diags = tfschema.CopyDatabaseV3ToTerraform(ctx, database, &plan) resp.Diagnostics.Append(diags...) @@ -177,7 +180,7 @@ func (r resourceTeleportDatabase) Read(ctx context.Context, req tfsdk.ReadResour resp.Diagnostics.Append(diagFromWrappedErr("Error reading Database", trace.Wrap(err), "db")) return } - + database := databaseI.(*apitypes.DatabaseV3) diags = tfschema.CopyDatabaseV3ToTerraform(ctx, database, &state) resp.Diagnostics.Append(diags...) @@ -212,14 +215,14 @@ func (r resourceTeleportDatabase) Update(ctx context.Context, req tfsdk.UpdateRe if resp.Diagnostics.HasError() { return } + databaseResource := database - name := database.Metadata.Name - err := database.CheckAndSetDefaults() - if err != nil { + if err := databaseResource.CheckAndSetDefaults(); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating Database", err, "db")) return } + name := databaseResource.Metadata.Name databaseBefore, err := r.p.Client.GetDatabase(ctx, name) if err != nil { @@ -227,12 +230,13 @@ func (r resourceTeleportDatabase) Update(ctx context.Context, req tfsdk.UpdateRe return } - err = r.p.Client.UpdateDatabase(ctx, database) + err = r.p.Client.UpdateDatabase(ctx, databaseResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating Database", err, "db")) return } - + + // Not really an inferface, just using the same name for easier templating. var databaseI apitypes.Database tries := 0 @@ -259,7 +263,11 @@ func (r resourceTeleportDatabase) Update(ctx context.Context, req tfsdk.UpdateRe } } - database = databaseI.(*apitypes.DatabaseV3) + databaseResource, ok := databaseI.(*apitypes.DatabaseV3) + if !ok { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading Database", trace.Errorf("Can not convert %T to DatabaseV3", databaseI), "db")) + return + } diags = tfschema.CopyDatabaseV3ToTerraform(ctx, database, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -293,13 +301,14 @@ func (r resourceTeleportDatabase) Delete(ctx context.Context, req tfsdk.DeleteRe // ImportState imports Database state func (r resourceTeleportDatabase) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - databaseI, err := r.p.Client.GetDatabase(ctx, req.ID) + database, err := r.p.Client.GetDatabase(ctx, req.ID) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading Database", trace.Wrap(err), "db")) return } - database := databaseI.(*apitypes.DatabaseV3) + + databaseResource := database.(*apitypes.DatabaseV3) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportDatabase) ImportState(ctx context.Context, req tfsdk.Imp return } - diags = tfschema.CopyDatabaseV3ToTerraform(ctx, database, &state) + diags = tfschema.CopyDatabaseV3ToTerraform(ctx, databaseResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := databaseResource.GetName() - state.Attrs["id"] = types.String{Value: database.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_device_trust.go b/terraform/provider/resource_teleport_device_trust.go index ef32ebab8..47c38c7fc 100755 --- a/terraform/provider/resource_teleport_device_trust.go +++ b/terraform/provider/resource_teleport_device_trust.go @@ -56,6 +56,7 @@ func (r resourceTeleportDeviceV1Type) NewResource(_ context.Context, p tfsdk.Pro // Create creates the DeviceV1 func (r resourceTeleportDeviceV1) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { + var err error if !r.p.IsConfigured(resp.Diagnostics) { return } @@ -78,13 +79,15 @@ func (r resourceTeleportDeviceV1) Create(ctx context.Context, req tfsdk.CreateRe trustedDevice.Metadata.Name = uuid.NewString() } + trustedDeviceResource := trustedDevice - _, err := r.p.Client.GetDeviceResource(ctx, trustedDevice.Metadata.Name) + id := trustedDeviceResource.Metadata.Name + + _, err = r.p.Client.GetDeviceResource(ctx, id) if !trace.IsNotFound(err) { if err == nil { - n := trustedDevice.Metadata.Name existErr := fmt.Sprintf("DeviceV1 exists in Teleport. Either remove it (tctl rm device/%v)"+ - " or import it to the existing state (terraform import teleport_device_trust.%v %v)", n, n, n) + " or import it to the existing state (terraform import teleport_device_trust.%v %v)", id, id, id) resp.Diagnostics.Append(diagFromErr("DeviceV1 exists in Teleport", trace.Errorf(existErr))) return @@ -96,17 +99,12 @@ func (r resourceTeleportDeviceV1) Create(ctx context.Context, req tfsdk.CreateRe - _, err = r.p.Client.UpsertDeviceResource(ctx, trustedDevice) + _, err = r.p.Client.UpsertDeviceResource(ctx, trustedDeviceResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating DeviceV1", trace.Wrap(err), "device")) return } - - id := trustedDevice.Metadata.Name - // Not really an inferface, just using the same name for easier templating. - var trustedDeviceI *apitypes.DeviceV1 - - + var trustedDeviceI *apitypes.DeviceV1 tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { @@ -132,8 +130,9 @@ func (r resourceTeleportDeviceV1) Create(ctx context.Context, req tfsdk.CreateRe return } - trustedDevice = trustedDeviceI + trustedDeviceResource = trustedDeviceI + trustedDevice = trustedDeviceResource diags = tfschema.CopyDeviceV1ToTerraform(ctx, trustedDevice, &plan) resp.Diagnostics.Append(diags...) @@ -176,7 +175,6 @@ func (r resourceTeleportDeviceV1) Read(ctx context.Context, req tfsdk.ReadResour resp.Diagnostics.Append(diagFromWrappedErr("Error reading DeviceV1", trace.Wrap(err), "device")) return } - trustedDevice := trustedDeviceI diags = tfschema.CopyDeviceV1ToTerraform(ctx, trustedDevice, &state) resp.Diagnostics.Append(diags...) @@ -211,10 +209,11 @@ func (r resourceTeleportDeviceV1) Update(ctx context.Context, req tfsdk.UpdateRe if resp.Diagnostics.HasError() { return } + trustedDeviceResource := trustedDevice - name := trustedDevice.Metadata.Name + name := trustedDeviceResource.Metadata.Name trustedDeviceBefore, err := r.p.Client.GetDeviceResource(ctx, name) if err != nil { @@ -222,27 +221,25 @@ func (r resourceTeleportDeviceV1) Update(ctx context.Context, req tfsdk.UpdateRe return } - _, err = r.p.Client.UpsertDeviceResource(ctx, trustedDevice) + _, err = r.p.Client.UpsertDeviceResource(ctx, trustedDeviceResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating DeviceV1", err, "device")) return } - - + var trustedDeviceI *apitypes.DeviceV1 tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { tries = tries + 1 - trustedDevice, err = r.p.Client.GetDeviceResource(ctx, name) + trustedDeviceI, err = r.p.Client.GetDeviceResource(ctx, name) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading DeviceV1", err, "device")) return } - if trustedDeviceBefore.GetMetadata().ID != trustedDevice.GetMetadata().ID || true { + if trustedDeviceBefore.GetMetadata().ID != trustedDeviceI.GetMetadata().ID || true { break } - if err := backoff.Do(ctx); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading DeviceV1", trace.Wrap(err), "device")) @@ -255,6 +252,8 @@ func (r resourceTeleportDeviceV1) Update(ctx context.Context, req tfsdk.UpdateRe } } + trustedDeviceResource = trustedDeviceI + diags = tfschema.CopyDeviceV1ToTerraform(ctx, trustedDevice, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -294,6 +293,7 @@ func (r resourceTeleportDeviceV1) ImportState(ctx context.Context, req tfsdk.Imp return } + trustedDeviceResource := trustedDevice var state types.Object @@ -304,13 +304,14 @@ func (r resourceTeleportDeviceV1) ImportState(ctx context.Context, req tfsdk.Imp return } - diags = tfschema.CopyDeviceV1ToTerraform(ctx, trustedDevice, &state) + diags = tfschema.CopyDeviceV1ToTerraform(ctx, trustedDeviceResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := trustedDevice.Metadata.Name - state.Attrs["id"] = types.String{Value: trustedDevice.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_github_connector.go b/terraform/provider/resource_teleport_github_connector.go index e89afcb56..ca5e17a39 100755 --- a/terraform/provider/resource_teleport_github_connector.go +++ b/terraform/provider/resource_teleport_github_connector.go @@ -55,6 +55,7 @@ func (r resourceTeleportGithubConnectorType) NewResource(_ context.Context, p tf // Create creates the GithubConnector func (r resourceTeleportGithubConnector) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { + var err error if !r.p.IsConfigured(resp.Diagnostics) { return } @@ -74,13 +75,15 @@ func (r resourceTeleportGithubConnector) Create(ctx context.Context, req tfsdk.C } + githubConnectorResource := githubConnector - _, err := r.p.Client.GetGithubConnector(ctx, githubConnector.Metadata.Name, true) + id := githubConnectorResource.Metadata.Name + + _, err = r.p.Client.GetGithubConnector(ctx, id, true) if !trace.IsNotFound(err) { if err == nil { - n := githubConnector.Metadata.Name existErr := fmt.Sprintf("GithubConnector exists in Teleport. Either remove it (tctl rm github/%v)"+ - " or import it to the existing state (terraform import teleport_github_connector.%v %v)", n, n, n) + " or import it to the existing state (terraform import teleport_github_connector.%v %v)", id, id, id) resp.Diagnostics.Append(diagFromErr("GithubConnector exists in Teleport", trace.Errorf(existErr))) return @@ -90,21 +93,20 @@ func (r resourceTeleportGithubConnector) Create(ctx context.Context, req tfsdk.C return } - err = githubConnector.CheckAndSetDefaults() + err = githubConnectorResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting GithubConnector defaults", trace.Wrap(err), "github")) return } - err = r.p.Client.UpsertGithubConnector(ctx, githubConnector) + err = r.p.Client.UpsertGithubConnector(ctx, githubConnectorResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating GithubConnector", trace.Wrap(err), "github")) return } - - id := githubConnector.Metadata.Name + + // Not really an inferface, just using the same name for easier templating. var githubConnectorI apitypes.GithubConnector - tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { @@ -130,11 +132,12 @@ func (r resourceTeleportGithubConnector) Create(ctx context.Context, req tfsdk.C return } - githubConnector, ok := githubConnectorI.(*apitypes.GithubConnectorV3) + githubConnectorResource, ok := githubConnectorI.(*apitypes.GithubConnectorV3) if !ok { resp.Diagnostics.Append(diagFromWrappedErr("Error reading GithubConnector", trace.Errorf("Can not convert %T to GithubConnectorV3", githubConnectorI), "github")) return } + githubConnector = githubConnectorResource diags = tfschema.CopyGithubConnectorV3ToTerraform(ctx, githubConnector, &plan) resp.Diagnostics.Append(diags...) @@ -177,7 +180,7 @@ func (r resourceTeleportGithubConnector) Read(ctx context.Context, req tfsdk.Rea resp.Diagnostics.Append(diagFromWrappedErr("Error reading GithubConnector", trace.Wrap(err), "github")) return } - + githubConnector := githubConnectorI.(*apitypes.GithubConnectorV3) diags = tfschema.CopyGithubConnectorV3ToTerraform(ctx, githubConnector, &state) resp.Diagnostics.Append(diags...) @@ -212,14 +215,14 @@ func (r resourceTeleportGithubConnector) Update(ctx context.Context, req tfsdk.U if resp.Diagnostics.HasError() { return } + githubConnectorResource := githubConnector - name := githubConnector.Metadata.Name - err := githubConnector.CheckAndSetDefaults() - if err != nil { + if err := githubConnectorResource.CheckAndSetDefaults(); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating GithubConnector", err, "github")) return } + name := githubConnectorResource.Metadata.Name githubConnectorBefore, err := r.p.Client.GetGithubConnector(ctx, name, true) if err != nil { @@ -227,12 +230,13 @@ func (r resourceTeleportGithubConnector) Update(ctx context.Context, req tfsdk.U return } - err = r.p.Client.UpsertGithubConnector(ctx, githubConnector) + err = r.p.Client.UpsertGithubConnector(ctx, githubConnectorResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating GithubConnector", err, "github")) return } - + + // Not really an inferface, just using the same name for easier templating. var githubConnectorI apitypes.GithubConnector tries := 0 @@ -259,7 +263,11 @@ func (r resourceTeleportGithubConnector) Update(ctx context.Context, req tfsdk.U } } - githubConnector = githubConnectorI.(*apitypes.GithubConnectorV3) + githubConnectorResource, ok := githubConnectorI.(*apitypes.GithubConnectorV3) + if !ok { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading GithubConnector", trace.Errorf("Can not convert %T to GithubConnectorV3", githubConnectorI), "github")) + return + } diags = tfschema.CopyGithubConnectorV3ToTerraform(ctx, githubConnector, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -293,13 +301,14 @@ func (r resourceTeleportGithubConnector) Delete(ctx context.Context, req tfsdk.D // ImportState imports GithubConnector state func (r resourceTeleportGithubConnector) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - githubConnectorI, err := r.p.Client.GetGithubConnector(ctx, req.ID, true) + githubConnector, err := r.p.Client.GetGithubConnector(ctx, req.ID, true) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading GithubConnector", trace.Wrap(err), "github")) return } - githubConnector := githubConnectorI.(*apitypes.GithubConnectorV3) + + githubConnectorResource := githubConnector.(*apitypes.GithubConnectorV3) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportGithubConnector) ImportState(ctx context.Context, req tf return } - diags = tfschema.CopyGithubConnectorV3ToTerraform(ctx, githubConnector, &state) + diags = tfschema.CopyGithubConnectorV3ToTerraform(ctx, githubConnectorResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := githubConnectorResource.GetName() - state.Attrs["id"] = types.String{Value: githubConnector.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_login_rule.go b/terraform/provider/resource_teleport_login_rule.go index bd91b6933..4ca5c2d1d 100755 --- a/terraform/provider/resource_teleport_login_rule.go +++ b/terraform/provider/resource_teleport_login_rule.go @@ -55,6 +55,7 @@ func (r resourceTeleportLoginRuleType) NewResource(_ context.Context, p tfsdk.Pr // Create creates the LoginRule func (r resourceTeleportLoginRule) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { + var err error if !r.p.IsConfigured(resp.Diagnostics) { return } @@ -74,13 +75,15 @@ func (r resourceTeleportLoginRule) Create(ctx context.Context, req tfsdk.CreateR } + loginRuleResource := loginRule - _, err := r.p.Client.GetLoginRule(ctx, loginRule.Metadata.Name) + id := loginRuleResource.Metadata.Name + + _, err = r.p.Client.GetLoginRule(ctx, id) if !trace.IsNotFound(err) { if err == nil { - n := loginRule.Metadata.Name existErr := fmt.Sprintf("LoginRule exists in Teleport. Either remove it (tctl rm login_rule/%v)"+ - " or import it to the existing state (terraform import teleport_login_rule.%v %v)", n, n, n) + " or import it to the existing state (terraform import teleport_login_rule.%v %v)", id, id, id) resp.Diagnostics.Append(diagFromErr("LoginRule exists in Teleport", trace.Errorf(existErr))) return @@ -92,17 +95,12 @@ func (r resourceTeleportLoginRule) Create(ctx context.Context, req tfsdk.CreateR - _, err = r.p.Client.UpsertLoginRule(ctx, loginRule) + _, err = r.p.Client.UpsertLoginRule(ctx, loginRuleResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating LoginRule", trace.Wrap(err), "login_rule")) return } - - id := loginRule.Metadata.Name - // Not really an inferface, just using the same name for easier templating. - var loginRuleI *loginrulev1.LoginRule - - + var loginRuleI *loginrulev1.LoginRule tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { @@ -128,8 +126,9 @@ func (r resourceTeleportLoginRule) Create(ctx context.Context, req tfsdk.CreateR return } - loginRule = loginRuleI + loginRuleResource = loginRuleI + loginRule = loginRuleResource diags = schemav1.CopyLoginRuleToTerraform(ctx, loginRule, &plan) resp.Diagnostics.Append(diags...) @@ -172,7 +171,6 @@ func (r resourceTeleportLoginRule) Read(ctx context.Context, req tfsdk.ReadResou resp.Diagnostics.Append(diagFromWrappedErr("Error reading LoginRule", trace.Wrap(err), "login_rule")) return } - loginRule := loginRuleI diags = schemav1.CopyLoginRuleToTerraform(ctx, loginRule, &state) resp.Diagnostics.Append(diags...) @@ -207,10 +205,11 @@ func (r resourceTeleportLoginRule) Update(ctx context.Context, req tfsdk.UpdateR if resp.Diagnostics.HasError() { return } + loginRuleResource := loginRule - name := loginRule.Metadata.Name + name := loginRuleResource.Metadata.Name loginRuleBefore, err := r.p.Client.GetLoginRule(ctx, name) if err != nil { @@ -218,27 +217,25 @@ func (r resourceTeleportLoginRule) Update(ctx context.Context, req tfsdk.UpdateR return } - _, err = r.p.Client.UpsertLoginRule(ctx, loginRule) + _, err = r.p.Client.UpsertLoginRule(ctx, loginRuleResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating LoginRule", err, "login_rule")) return } - - + var loginRuleI *loginrulev1.LoginRule tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { tries = tries + 1 - loginRule, err = r.p.Client.GetLoginRule(ctx, name) + loginRuleI, err = r.p.Client.GetLoginRule(ctx, name) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading LoginRule", err, "login_rule")) return } - if loginRuleBefore.GetMetadata().ID != loginRule.GetMetadata().ID || false { + if loginRuleBefore.GetMetadata().ID != loginRuleI.GetMetadata().ID || false { break } - if err := backoff.Do(ctx); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading LoginRule", trace.Wrap(err), "login_rule")) @@ -251,6 +248,8 @@ func (r resourceTeleportLoginRule) Update(ctx context.Context, req tfsdk.UpdateR } } + loginRuleResource = loginRuleI + diags = schemav1.CopyLoginRuleToTerraform(ctx, loginRule, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -290,6 +289,7 @@ func (r resourceTeleportLoginRule) ImportState(ctx context.Context, req tfsdk.Im return } + loginRuleResource := loginRule var state types.Object @@ -300,13 +300,14 @@ func (r resourceTeleportLoginRule) ImportState(ctx context.Context, req tfsdk.Im return } - diags = schemav1.CopyLoginRuleToTerraform(ctx, loginRule, &state) + diags = schemav1.CopyLoginRuleToTerraform(ctx, loginRuleResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := loginRule.Metadata.Name - state.Attrs["id"] = types.String{Value: loginRule.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_oidc_connector.go b/terraform/provider/resource_teleport_oidc_connector.go index 8a41f5901..6253f45c7 100755 --- a/terraform/provider/resource_teleport_oidc_connector.go +++ b/terraform/provider/resource_teleport_oidc_connector.go @@ -55,6 +55,7 @@ func (r resourceTeleportOIDCConnectorType) NewResource(_ context.Context, p tfsd // Create creates the OIDCConnector func (r resourceTeleportOIDCConnector) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { + var err error if !r.p.IsConfigured(resp.Diagnostics) { return } @@ -74,13 +75,15 @@ func (r resourceTeleportOIDCConnector) Create(ctx context.Context, req tfsdk.Cre } + oidcConnectorResource := oidcConnector - _, err := r.p.Client.GetOIDCConnector(ctx, oidcConnector.Metadata.Name, true) + id := oidcConnectorResource.Metadata.Name + + _, err = r.p.Client.GetOIDCConnector(ctx, id, true) if !trace.IsNotFound(err) { if err == nil { - n := oidcConnector.Metadata.Name existErr := fmt.Sprintf("OIDCConnector exists in Teleport. Either remove it (tctl rm oidc/%v)"+ - " or import it to the existing state (terraform import teleport_oidc_connector.%v %v)", n, n, n) + " or import it to the existing state (terraform import teleport_oidc_connector.%v %v)", id, id, id) resp.Diagnostics.Append(diagFromErr("OIDCConnector exists in Teleport", trace.Errorf(existErr))) return @@ -90,21 +93,20 @@ func (r resourceTeleportOIDCConnector) Create(ctx context.Context, req tfsdk.Cre return } - err = oidcConnector.CheckAndSetDefaults() + err = oidcConnectorResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting OIDCConnector defaults", trace.Wrap(err), "oidc")) return } - err = r.p.Client.UpsertOIDCConnector(ctx, oidcConnector) + err = r.p.Client.UpsertOIDCConnector(ctx, oidcConnectorResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating OIDCConnector", trace.Wrap(err), "oidc")) return } - - id := oidcConnector.Metadata.Name + + // Not really an inferface, just using the same name for easier templating. var oidcConnectorI apitypes.OIDCConnector - tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { @@ -130,11 +132,12 @@ func (r resourceTeleportOIDCConnector) Create(ctx context.Context, req tfsdk.Cre return } - oidcConnector, ok := oidcConnectorI.(*apitypes.OIDCConnectorV3) + oidcConnectorResource, ok := oidcConnectorI.(*apitypes.OIDCConnectorV3) if !ok { resp.Diagnostics.Append(diagFromWrappedErr("Error reading OIDCConnector", trace.Errorf("Can not convert %T to OIDCConnectorV3", oidcConnectorI), "oidc")) return } + oidcConnector = oidcConnectorResource diags = tfschema.CopyOIDCConnectorV3ToTerraform(ctx, oidcConnector, &plan) resp.Diagnostics.Append(diags...) @@ -177,7 +180,7 @@ func (r resourceTeleportOIDCConnector) Read(ctx context.Context, req tfsdk.ReadR resp.Diagnostics.Append(diagFromWrappedErr("Error reading OIDCConnector", trace.Wrap(err), "oidc")) return } - + oidcConnector := oidcConnectorI.(*apitypes.OIDCConnectorV3) diags = tfschema.CopyOIDCConnectorV3ToTerraform(ctx, oidcConnector, &state) resp.Diagnostics.Append(diags...) @@ -212,14 +215,14 @@ func (r resourceTeleportOIDCConnector) Update(ctx context.Context, req tfsdk.Upd if resp.Diagnostics.HasError() { return } + oidcConnectorResource := oidcConnector - name := oidcConnector.Metadata.Name - err := oidcConnector.CheckAndSetDefaults() - if err != nil { + if err := oidcConnectorResource.CheckAndSetDefaults(); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating OIDCConnector", err, "oidc")) return } + name := oidcConnectorResource.Metadata.Name oidcConnectorBefore, err := r.p.Client.GetOIDCConnector(ctx, name, true) if err != nil { @@ -227,12 +230,13 @@ func (r resourceTeleportOIDCConnector) Update(ctx context.Context, req tfsdk.Upd return } - err = r.p.Client.UpsertOIDCConnector(ctx, oidcConnector) + err = r.p.Client.UpsertOIDCConnector(ctx, oidcConnectorResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating OIDCConnector", err, "oidc")) return } - + + // Not really an inferface, just using the same name for easier templating. var oidcConnectorI apitypes.OIDCConnector tries := 0 @@ -259,7 +263,11 @@ func (r resourceTeleportOIDCConnector) Update(ctx context.Context, req tfsdk.Upd } } - oidcConnector = oidcConnectorI.(*apitypes.OIDCConnectorV3) + oidcConnectorResource, ok := oidcConnectorI.(*apitypes.OIDCConnectorV3) + if !ok { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading OIDCConnector", trace.Errorf("Can not convert %T to OIDCConnectorV3", oidcConnectorI), "oidc")) + return + } diags = tfschema.CopyOIDCConnectorV3ToTerraform(ctx, oidcConnector, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -293,13 +301,14 @@ func (r resourceTeleportOIDCConnector) Delete(ctx context.Context, req tfsdk.Del // ImportState imports OIDCConnector state func (r resourceTeleportOIDCConnector) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - oidcConnectorI, err := r.p.Client.GetOIDCConnector(ctx, req.ID, true) + oidcConnector, err := r.p.Client.GetOIDCConnector(ctx, req.ID, true) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading OIDCConnector", trace.Wrap(err), "oidc")) return } - oidcConnector := oidcConnectorI.(*apitypes.OIDCConnectorV3) + + oidcConnectorResource := oidcConnector.(*apitypes.OIDCConnectorV3) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportOIDCConnector) ImportState(ctx context.Context, req tfsd return } - diags = tfschema.CopyOIDCConnectorV3ToTerraform(ctx, oidcConnector, &state) + diags = tfschema.CopyOIDCConnectorV3ToTerraform(ctx, oidcConnectorResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := oidcConnectorResource.GetName() - state.Attrs["id"] = types.String{Value: oidcConnector.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_okta_import_rule.go b/terraform/provider/resource_teleport_okta_import_rule.go index 7897a7a2c..0b0ab51a2 100755 --- a/terraform/provider/resource_teleport_okta_import_rule.go +++ b/terraform/provider/resource_teleport_okta_import_rule.go @@ -55,6 +55,7 @@ func (r resourceTeleportOktaImportRuleType) NewResource(_ context.Context, p tfs // Create creates the OktaImportRule func (r resourceTeleportOktaImportRule) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { + var err error if !r.p.IsConfigured(resp.Diagnostics) { return } @@ -74,13 +75,15 @@ func (r resourceTeleportOktaImportRule) Create(ctx context.Context, req tfsdk.Cr } + oktaImportRuleResource := oktaImportRule - _, err := r.p.Client.OktaClient().GetOktaImportRule(ctx, oktaImportRule.Metadata.Name) + id := oktaImportRuleResource.Metadata.Name + + _, err = r.p.Client.OktaClient().GetOktaImportRule(ctx, id) if !trace.IsNotFound(err) { if err == nil { - n := oktaImportRule.Metadata.Name existErr := fmt.Sprintf("OktaImportRule exists in Teleport. Either remove it (tctl rm okta_import_rule/%v)"+ - " or import it to the existing state (terraform import teleport_okta_import_rule.%v %v)", n, n, n) + " or import it to the existing state (terraform import teleport_okta_import_rule.%v %v)", id, id, id) resp.Diagnostics.Append(diagFromErr("OktaImportRule exists in Teleport", trace.Errorf(existErr))) return @@ -90,21 +93,20 @@ func (r resourceTeleportOktaImportRule) Create(ctx context.Context, req tfsdk.Cr return } - err = oktaImportRule.CheckAndSetDefaults() + err = oktaImportRuleResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting OktaImportRule defaults", trace.Wrap(err), "okta_import_rule")) return } - _, err = r.p.Client.OktaClient().CreateOktaImportRule(ctx, oktaImportRule) + _, err = r.p.Client.OktaClient().CreateOktaImportRule(ctx, oktaImportRuleResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating OktaImportRule", trace.Wrap(err), "okta_import_rule")) return } - - id := oktaImportRule.Metadata.Name + + // Not really an inferface, just using the same name for easier templating. var oktaImportRuleI apitypes.OktaImportRule - tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { @@ -130,11 +132,12 @@ func (r resourceTeleportOktaImportRule) Create(ctx context.Context, req tfsdk.Cr return } - oktaImportRule, ok := oktaImportRuleI.(*apitypes.OktaImportRuleV1) + oktaImportRuleResource, ok := oktaImportRuleI.(*apitypes.OktaImportRuleV1) if !ok { resp.Diagnostics.Append(diagFromWrappedErr("Error reading OktaImportRule", trace.Errorf("Can not convert %T to OktaImportRuleV1", oktaImportRuleI), "okta_import_rule")) return } + oktaImportRule = oktaImportRuleResource diags = tfschema.CopyOktaImportRuleV1ToTerraform(ctx, oktaImportRule, &plan) resp.Diagnostics.Append(diags...) @@ -177,7 +180,7 @@ func (r resourceTeleportOktaImportRule) Read(ctx context.Context, req tfsdk.Read resp.Diagnostics.Append(diagFromWrappedErr("Error reading OktaImportRule", trace.Wrap(err), "okta_import_rule")) return } - + oktaImportRule := oktaImportRuleI.(*apitypes.OktaImportRuleV1) diags = tfschema.CopyOktaImportRuleV1ToTerraform(ctx, oktaImportRule, &state) resp.Diagnostics.Append(diags...) @@ -212,14 +215,14 @@ func (r resourceTeleportOktaImportRule) Update(ctx context.Context, req tfsdk.Up if resp.Diagnostics.HasError() { return } + oktaImportRuleResource := oktaImportRule - name := oktaImportRule.Metadata.Name - err := oktaImportRule.CheckAndSetDefaults() - if err != nil { + if err := oktaImportRuleResource.CheckAndSetDefaults(); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating OktaImportRule", err, "okta_import_rule")) return } + name := oktaImportRuleResource.Metadata.Name oktaImportRuleBefore, err := r.p.Client.OktaClient().GetOktaImportRule(ctx, name) if err != nil { @@ -227,12 +230,13 @@ func (r resourceTeleportOktaImportRule) Update(ctx context.Context, req tfsdk.Up return } - _, err = r.p.Client.OktaClient().UpdateOktaImportRule(ctx, oktaImportRule) + _, err = r.p.Client.OktaClient().UpdateOktaImportRule(ctx, oktaImportRuleResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating OktaImportRule", err, "okta_import_rule")) return } - + + // Not really an inferface, just using the same name for easier templating. var oktaImportRuleI apitypes.OktaImportRule tries := 0 @@ -259,7 +263,11 @@ func (r resourceTeleportOktaImportRule) Update(ctx context.Context, req tfsdk.Up } } - oktaImportRule = oktaImportRuleI.(*apitypes.OktaImportRuleV1) + oktaImportRuleResource, ok := oktaImportRuleI.(*apitypes.OktaImportRuleV1) + if !ok { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading OktaImportRule", trace.Errorf("Can not convert %T to OktaImportRuleV1", oktaImportRuleI), "okta_import_rule")) + return + } diags = tfschema.CopyOktaImportRuleV1ToTerraform(ctx, oktaImportRule, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -293,13 +301,14 @@ func (r resourceTeleportOktaImportRule) Delete(ctx context.Context, req tfsdk.De // ImportState imports OktaImportRule state func (r resourceTeleportOktaImportRule) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - oktaImportRuleI, err := r.p.Client.OktaClient().GetOktaImportRule(ctx, req.ID) + oktaImportRule, err := r.p.Client.OktaClient().GetOktaImportRule(ctx, req.ID) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading OktaImportRule", trace.Wrap(err), "okta_import_rule")) return } - oktaImportRule := oktaImportRuleI.(*apitypes.OktaImportRuleV1) + + oktaImportRuleResource := oktaImportRule.(*apitypes.OktaImportRuleV1) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportOktaImportRule) ImportState(ctx context.Context, req tfs return } - diags = tfschema.CopyOktaImportRuleV1ToTerraform(ctx, oktaImportRule, &state) + diags = tfschema.CopyOktaImportRuleV1ToTerraform(ctx, oktaImportRuleResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := oktaImportRuleResource.GetName() - state.Attrs["id"] = types.String{Value: oktaImportRule.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_provision_token.go b/terraform/provider/resource_teleport_provision_token.go index 6d58de281..e599ff4b7 100755 --- a/terraform/provider/resource_teleport_provision_token.go +++ b/terraform/provider/resource_teleport_provision_token.go @@ -58,6 +58,7 @@ func (r resourceTeleportProvisionTokenType) NewResource(_ context.Context, p tfs // Create creates the ProvisionToken func (r resourceTeleportProvisionToken) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { + var err error if !r.p.IsConfigured(resp.Diagnostics) { return } @@ -86,13 +87,15 @@ func (r resourceTeleportProvisionToken) Create(ctx context.Context, req tfsdk.Cr provisionToken.Metadata.Name = hex.EncodeToString(b) } + provisionTokenResource := provisionToken - _, err := r.p.Client.GetToken(ctx, provisionToken.Metadata.Name) + id := provisionTokenResource.Metadata.Name + + _, err = r.p.Client.GetToken(ctx, id) if !trace.IsNotFound(err) { if err == nil { - n := provisionToken.Metadata.Name existErr := fmt.Sprintf("ProvisionToken exists in Teleport. Either remove it (tctl rm token/%v)"+ - " or import it to the existing state (terraform import teleport_provision_token.%v %v)", n, n, n) + " or import it to the existing state (terraform import teleport_provision_token.%v %v)", id, id, id) resp.Diagnostics.Append(diagFromErr("ProvisionToken exists in Teleport", trace.Errorf(existErr))) return @@ -102,21 +105,20 @@ func (r resourceTeleportProvisionToken) Create(ctx context.Context, req tfsdk.Cr return } - err = provisionToken.CheckAndSetDefaults() + err = provisionTokenResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting ProvisionToken defaults", trace.Wrap(err), "token")) return } - err = r.p.Client.UpsertToken(ctx, provisionToken) + err = r.p.Client.UpsertToken(ctx, provisionTokenResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating ProvisionToken", trace.Wrap(err), "token")) return } - - id := provisionToken.Metadata.Name + + // Not really an inferface, just using the same name for easier templating. var provisionTokenI apitypes.ProvisionToken - tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { @@ -142,11 +144,12 @@ func (r resourceTeleportProvisionToken) Create(ctx context.Context, req tfsdk.Cr return } - provisionToken, ok := provisionTokenI.(*apitypes.ProvisionTokenV2) + provisionTokenResource, ok := provisionTokenI.(*apitypes.ProvisionTokenV2) if !ok { resp.Diagnostics.Append(diagFromWrappedErr("Error reading ProvisionToken", trace.Errorf("Can not convert %T to ProvisionTokenV2", provisionTokenI), "token")) return } + provisionToken = provisionTokenResource diags = tfschema.CopyProvisionTokenV2ToTerraform(ctx, provisionToken, &plan) resp.Diagnostics.Append(diags...) @@ -189,7 +192,7 @@ func (r resourceTeleportProvisionToken) Read(ctx context.Context, req tfsdk.Read resp.Diagnostics.Append(diagFromWrappedErr("Error reading ProvisionToken", trace.Wrap(err), "token")) return } - + provisionToken := provisionTokenI.(*apitypes.ProvisionTokenV2) diags = tfschema.CopyProvisionTokenV2ToTerraform(ctx, provisionToken, &state) resp.Diagnostics.Append(diags...) @@ -224,14 +227,14 @@ func (r resourceTeleportProvisionToken) Update(ctx context.Context, req tfsdk.Up if resp.Diagnostics.HasError() { return } + provisionTokenResource := provisionToken - name := provisionToken.Metadata.Name - err := provisionToken.CheckAndSetDefaults() - if err != nil { + if err := provisionTokenResource.CheckAndSetDefaults(); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating ProvisionToken", err, "token")) return } + name := provisionTokenResource.Metadata.Name provisionTokenBefore, err := r.p.Client.GetToken(ctx, name) if err != nil { @@ -239,12 +242,13 @@ func (r resourceTeleportProvisionToken) Update(ctx context.Context, req tfsdk.Up return } - err = r.p.Client.UpsertToken(ctx, provisionToken) + err = r.p.Client.UpsertToken(ctx, provisionTokenResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating ProvisionToken", err, "token")) return } - + + // Not really an inferface, just using the same name for easier templating. var provisionTokenI apitypes.ProvisionToken tries := 0 @@ -271,7 +275,11 @@ func (r resourceTeleportProvisionToken) Update(ctx context.Context, req tfsdk.Up } } - provisionToken = provisionTokenI.(*apitypes.ProvisionTokenV2) + provisionTokenResource, ok := provisionTokenI.(*apitypes.ProvisionTokenV2) + if !ok { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading ProvisionToken", trace.Errorf("Can not convert %T to ProvisionTokenV2", provisionTokenI), "token")) + return + } diags = tfschema.CopyProvisionTokenV2ToTerraform(ctx, provisionToken, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -305,13 +313,14 @@ func (r resourceTeleportProvisionToken) Delete(ctx context.Context, req tfsdk.De // ImportState imports ProvisionToken state func (r resourceTeleportProvisionToken) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - provisionTokenI, err := r.p.Client.GetToken(ctx, req.ID) + provisionToken, err := r.p.Client.GetToken(ctx, req.ID) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading ProvisionToken", trace.Wrap(err), "token")) return } - provisionToken := provisionTokenI.(*apitypes.ProvisionTokenV2) + + provisionTokenResource := provisionToken.(*apitypes.ProvisionTokenV2) var state types.Object @@ -321,13 +330,14 @@ func (r resourceTeleportProvisionToken) ImportState(ctx context.Context, req tfs return } - diags = tfschema.CopyProvisionTokenV2ToTerraform(ctx, provisionToken, &state) + diags = tfschema.CopyProvisionTokenV2ToTerraform(ctx, provisionTokenResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := provisionTokenResource.GetName() - state.Attrs["id"] = types.String{Value: provisionToken.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_role.go b/terraform/provider/resource_teleport_role.go index 7c1b62617..81b0df7c9 100755 --- a/terraform/provider/resource_teleport_role.go +++ b/terraform/provider/resource_teleport_role.go @@ -55,6 +55,7 @@ func (r resourceTeleportRoleType) NewResource(_ context.Context, p tfsdk.Provide // Create creates the Role func (r resourceTeleportRole) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { + var err error if !r.p.IsConfigured(resp.Diagnostics) { return } @@ -74,13 +75,15 @@ func (r resourceTeleportRole) Create(ctx context.Context, req tfsdk.CreateResour } + roleResource := role - _, err := r.p.Client.GetRole(ctx, role.Metadata.Name) + id := roleResource.Metadata.Name + + _, err = r.p.Client.GetRole(ctx, id) if !trace.IsNotFound(err) { if err == nil { - n := role.Metadata.Name existErr := fmt.Sprintf("Role exists in Teleport. Either remove it (tctl rm role/%v)"+ - " or import it to the existing state (terraform import teleport_role.%v %v)", n, n, n) + " or import it to the existing state (terraform import teleport_role.%v %v)", id, id, id) resp.Diagnostics.Append(diagFromErr("Role exists in Teleport", trace.Errorf(existErr))) return @@ -90,21 +93,20 @@ func (r resourceTeleportRole) Create(ctx context.Context, req tfsdk.CreateResour return } - err = role.CheckAndSetDefaults() + err = roleResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting Role defaults", trace.Wrap(err), "role")) return } - err = r.p.Client.UpsertRole(ctx, role) + err = r.p.Client.UpsertRole(ctx, roleResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating Role", trace.Wrap(err), "role")) return } - - id := role.Metadata.Name + + // Not really an inferface, just using the same name for easier templating. var roleI apitypes.Role - tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { @@ -130,11 +132,12 @@ func (r resourceTeleportRole) Create(ctx context.Context, req tfsdk.CreateResour return } - role, ok := roleI.(*apitypes.RoleV6) + roleResource, ok := roleI.(*apitypes.RoleV6) if !ok { resp.Diagnostics.Append(diagFromWrappedErr("Error reading Role", trace.Errorf("Can not convert %T to RoleV6", roleI), "role")) return } + role = roleResource diags = tfschema.CopyRoleV6ToTerraform(ctx, role, &plan) resp.Diagnostics.Append(diags...) @@ -177,7 +180,7 @@ func (r resourceTeleportRole) Read(ctx context.Context, req tfsdk.ReadResourceRe resp.Diagnostics.Append(diagFromWrappedErr("Error reading Role", trace.Wrap(err), "role")) return } - + role := roleI.(*apitypes.RoleV6) diags = tfschema.CopyRoleV6ToTerraform(ctx, role, &state) resp.Diagnostics.Append(diags...) @@ -212,14 +215,14 @@ func (r resourceTeleportRole) Update(ctx context.Context, req tfsdk.UpdateResour if resp.Diagnostics.HasError() { return } + roleResource := role - name := role.Metadata.Name - err := role.CheckAndSetDefaults() - if err != nil { + if err := roleResource.CheckAndSetDefaults(); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating Role", err, "role")) return } + name := roleResource.Metadata.Name roleBefore, err := r.p.Client.GetRole(ctx, name) if err != nil { @@ -227,12 +230,13 @@ func (r resourceTeleportRole) Update(ctx context.Context, req tfsdk.UpdateResour return } - err = r.p.Client.UpsertRole(ctx, role) + err = r.p.Client.UpsertRole(ctx, roleResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating Role", err, "role")) return } - + + // Not really an inferface, just using the same name for easier templating. var roleI apitypes.Role tries := 0 @@ -259,7 +263,11 @@ func (r resourceTeleportRole) Update(ctx context.Context, req tfsdk.UpdateResour } } - role = roleI.(*apitypes.RoleV6) + roleResource, ok := roleI.(*apitypes.RoleV6) + if !ok { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading Role", trace.Errorf("Can not convert %T to RoleV6", roleI), "role")) + return + } diags = tfschema.CopyRoleV6ToTerraform(ctx, role, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -293,13 +301,14 @@ func (r resourceTeleportRole) Delete(ctx context.Context, req tfsdk.DeleteResour // ImportState imports Role state func (r resourceTeleportRole) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - roleI, err := r.p.Client.GetRole(ctx, req.ID) + role, err := r.p.Client.GetRole(ctx, req.ID) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading Role", trace.Wrap(err), "role")) return } - role := roleI.(*apitypes.RoleV6) + + roleResource := role.(*apitypes.RoleV6) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportRole) ImportState(ctx context.Context, req tfsdk.ImportR return } - diags = tfschema.CopyRoleV6ToTerraform(ctx, role, &state) + diags = tfschema.CopyRoleV6ToTerraform(ctx, roleResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := roleResource.GetName() - state.Attrs["id"] = types.String{Value: role.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_saml_connector.go b/terraform/provider/resource_teleport_saml_connector.go index 64d7e268e..e0d93cbb2 100755 --- a/terraform/provider/resource_teleport_saml_connector.go +++ b/terraform/provider/resource_teleport_saml_connector.go @@ -55,6 +55,7 @@ func (r resourceTeleportSAMLConnectorType) NewResource(_ context.Context, p tfsd // Create creates the SAMLConnector func (r resourceTeleportSAMLConnector) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { + var err error if !r.p.IsConfigured(resp.Diagnostics) { return } @@ -74,13 +75,15 @@ func (r resourceTeleportSAMLConnector) Create(ctx context.Context, req tfsdk.Cre } + samlConnectorResource := samlConnector - _, err := r.p.Client.GetSAMLConnector(ctx, samlConnector.Metadata.Name, true) + id := samlConnectorResource.Metadata.Name + + _, err = r.p.Client.GetSAMLConnector(ctx, id, true) if !trace.IsNotFound(err) { if err == nil { - n := samlConnector.Metadata.Name existErr := fmt.Sprintf("SAMLConnector exists in Teleport. Either remove it (tctl rm saml/%v)"+ - " or import it to the existing state (terraform import teleport_saml_connector.%v %v)", n, n, n) + " or import it to the existing state (terraform import teleport_saml_connector.%v %v)", id, id, id) resp.Diagnostics.Append(diagFromErr("SAMLConnector exists in Teleport", trace.Errorf(existErr))) return @@ -90,21 +93,20 @@ func (r resourceTeleportSAMLConnector) Create(ctx context.Context, req tfsdk.Cre return } - err = samlConnector.CheckAndSetDefaults() + err = samlConnectorResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting SAMLConnector defaults", trace.Wrap(err), "saml")) return } - err = r.p.Client.UpsertSAMLConnector(ctx, samlConnector) + err = r.p.Client.UpsertSAMLConnector(ctx, samlConnectorResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating SAMLConnector", trace.Wrap(err), "saml")) return } - - id := samlConnector.Metadata.Name + + // Not really an inferface, just using the same name for easier templating. var samlConnectorI apitypes.SAMLConnector - tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { @@ -130,11 +132,12 @@ func (r resourceTeleportSAMLConnector) Create(ctx context.Context, req tfsdk.Cre return } - samlConnector, ok := samlConnectorI.(*apitypes.SAMLConnectorV2) + samlConnectorResource, ok := samlConnectorI.(*apitypes.SAMLConnectorV2) if !ok { resp.Diagnostics.Append(diagFromWrappedErr("Error reading SAMLConnector", trace.Errorf("Can not convert %T to SAMLConnectorV2", samlConnectorI), "saml")) return } + samlConnector = samlConnectorResource diags = tfschema.CopySAMLConnectorV2ToTerraform(ctx, samlConnector, &plan) resp.Diagnostics.Append(diags...) @@ -177,7 +180,7 @@ func (r resourceTeleportSAMLConnector) Read(ctx context.Context, req tfsdk.ReadR resp.Diagnostics.Append(diagFromWrappedErr("Error reading SAMLConnector", trace.Wrap(err), "saml")) return } - + samlConnector := samlConnectorI.(*apitypes.SAMLConnectorV2) diags = tfschema.CopySAMLConnectorV2ToTerraform(ctx, samlConnector, &state) resp.Diagnostics.Append(diags...) @@ -212,14 +215,14 @@ func (r resourceTeleportSAMLConnector) Update(ctx context.Context, req tfsdk.Upd if resp.Diagnostics.HasError() { return } + samlConnectorResource := samlConnector - name := samlConnector.Metadata.Name - err := samlConnector.CheckAndSetDefaults() - if err != nil { + if err := samlConnectorResource.CheckAndSetDefaults(); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating SAMLConnector", err, "saml")) return } + name := samlConnectorResource.Metadata.Name samlConnectorBefore, err := r.p.Client.GetSAMLConnector(ctx, name, true) if err != nil { @@ -227,12 +230,13 @@ func (r resourceTeleportSAMLConnector) Update(ctx context.Context, req tfsdk.Upd return } - err = r.p.Client.UpsertSAMLConnector(ctx, samlConnector) + err = r.p.Client.UpsertSAMLConnector(ctx, samlConnectorResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating SAMLConnector", err, "saml")) return } - + + // Not really an inferface, just using the same name for easier templating. var samlConnectorI apitypes.SAMLConnector tries := 0 @@ -259,7 +263,11 @@ func (r resourceTeleportSAMLConnector) Update(ctx context.Context, req tfsdk.Upd } } - samlConnector = samlConnectorI.(*apitypes.SAMLConnectorV2) + samlConnectorResource, ok := samlConnectorI.(*apitypes.SAMLConnectorV2) + if !ok { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading SAMLConnector", trace.Errorf("Can not convert %T to SAMLConnectorV2", samlConnectorI), "saml")) + return + } diags = tfschema.CopySAMLConnectorV2ToTerraform(ctx, samlConnector, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -293,13 +301,14 @@ func (r resourceTeleportSAMLConnector) Delete(ctx context.Context, req tfsdk.Del // ImportState imports SAMLConnector state func (r resourceTeleportSAMLConnector) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - samlConnectorI, err := r.p.Client.GetSAMLConnector(ctx, req.ID, true) + samlConnector, err := r.p.Client.GetSAMLConnector(ctx, req.ID, true) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading SAMLConnector", trace.Wrap(err), "saml")) return } - samlConnector := samlConnectorI.(*apitypes.SAMLConnectorV2) + + samlConnectorResource := samlConnector.(*apitypes.SAMLConnectorV2) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportSAMLConnector) ImportState(ctx context.Context, req tfsd return } - diags = tfschema.CopySAMLConnectorV2ToTerraform(ctx, samlConnector, &state) + diags = tfschema.CopySAMLConnectorV2ToTerraform(ctx, samlConnectorResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := samlConnectorResource.GetName() - state.Attrs["id"] = types.String{Value: samlConnector.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_trusted_cluster.go b/terraform/provider/resource_teleport_trusted_cluster.go index 7b3a0a5f9..612066eb3 100755 --- a/terraform/provider/resource_teleport_trusted_cluster.go +++ b/terraform/provider/resource_teleport_trusted_cluster.go @@ -55,6 +55,7 @@ func (r resourceTeleportTrustedClusterType) NewResource(_ context.Context, p tfs // Create creates the TrustedCluster func (r resourceTeleportTrustedCluster) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { + var err error if !r.p.IsConfigured(resp.Diagnostics) { return } @@ -74,13 +75,15 @@ func (r resourceTeleportTrustedCluster) Create(ctx context.Context, req tfsdk.Cr } + trustedClusterResource := trustedCluster - _, err := r.p.Client.GetTrustedCluster(ctx, trustedCluster.Metadata.Name) + id := trustedClusterResource.Metadata.Name + + _, err = r.p.Client.GetTrustedCluster(ctx, id) if !trace.IsNotFound(err) { if err == nil { - n := trustedCluster.Metadata.Name existErr := fmt.Sprintf("TrustedCluster exists in Teleport. Either remove it (tctl rm trusted_cluster/%v)"+ - " or import it to the existing state (terraform import teleport_trusted_cluster.%v %v)", n, n, n) + " or import it to the existing state (terraform import teleport_trusted_cluster.%v %v)", id, id, id) resp.Diagnostics.Append(diagFromErr("TrustedCluster exists in Teleport", trace.Errorf(existErr))) return @@ -90,21 +93,20 @@ func (r resourceTeleportTrustedCluster) Create(ctx context.Context, req tfsdk.Cr return } - err = trustedCluster.CheckAndSetDefaults() + err = trustedClusterResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting TrustedCluster defaults", trace.Wrap(err), "trusted_cluster")) return } - _, err = r.p.Client.UpsertTrustedCluster(ctx, trustedCluster) + _, err = r.p.Client.UpsertTrustedCluster(ctx, trustedClusterResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating TrustedCluster", trace.Wrap(err), "trusted_cluster")) return } - - id := trustedCluster.Metadata.Name + + // Not really an inferface, just using the same name for easier templating. var trustedClusterI apitypes.TrustedCluster - tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { @@ -130,11 +132,12 @@ func (r resourceTeleportTrustedCluster) Create(ctx context.Context, req tfsdk.Cr return } - trustedCluster, ok := trustedClusterI.(*apitypes.TrustedClusterV2) + trustedClusterResource, ok := trustedClusterI.(*apitypes.TrustedClusterV2) if !ok { resp.Diagnostics.Append(diagFromWrappedErr("Error reading TrustedCluster", trace.Errorf("Can not convert %T to TrustedClusterV2", trustedClusterI), "trusted_cluster")) return } + trustedCluster = trustedClusterResource diags = tfschema.CopyTrustedClusterV2ToTerraform(ctx, trustedCluster, &plan) resp.Diagnostics.Append(diags...) @@ -177,7 +180,7 @@ func (r resourceTeleportTrustedCluster) Read(ctx context.Context, req tfsdk.Read resp.Diagnostics.Append(diagFromWrappedErr("Error reading TrustedCluster", trace.Wrap(err), "trusted_cluster")) return } - + trustedCluster := trustedClusterI.(*apitypes.TrustedClusterV2) diags = tfschema.CopyTrustedClusterV2ToTerraform(ctx, trustedCluster, &state) resp.Diagnostics.Append(diags...) @@ -212,14 +215,14 @@ func (r resourceTeleportTrustedCluster) Update(ctx context.Context, req tfsdk.Up if resp.Diagnostics.HasError() { return } + trustedClusterResource := trustedCluster - name := trustedCluster.Metadata.Name - err := trustedCluster.CheckAndSetDefaults() - if err != nil { + if err := trustedClusterResource.CheckAndSetDefaults(); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating TrustedCluster", err, "trusted_cluster")) return } + name := trustedClusterResource.Metadata.Name trustedClusterBefore, err := r.p.Client.GetTrustedCluster(ctx, name) if err != nil { @@ -227,12 +230,13 @@ func (r resourceTeleportTrustedCluster) Update(ctx context.Context, req tfsdk.Up return } - _, err = r.p.Client.UpsertTrustedCluster(ctx, trustedCluster) + _, err = r.p.Client.UpsertTrustedCluster(ctx, trustedClusterResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating TrustedCluster", err, "trusted_cluster")) return } - + + // Not really an inferface, just using the same name for easier templating. var trustedClusterI apitypes.TrustedCluster tries := 0 @@ -259,7 +263,11 @@ func (r resourceTeleportTrustedCluster) Update(ctx context.Context, req tfsdk.Up } } - trustedCluster = trustedClusterI.(*apitypes.TrustedClusterV2) + trustedClusterResource, ok := trustedClusterI.(*apitypes.TrustedClusterV2) + if !ok { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading TrustedCluster", trace.Errorf("Can not convert %T to TrustedClusterV2", trustedClusterI), "trusted_cluster")) + return + } diags = tfschema.CopyTrustedClusterV2ToTerraform(ctx, trustedCluster, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -293,13 +301,14 @@ func (r resourceTeleportTrustedCluster) Delete(ctx context.Context, req tfsdk.De // ImportState imports TrustedCluster state func (r resourceTeleportTrustedCluster) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - trustedClusterI, err := r.p.Client.GetTrustedCluster(ctx, req.ID) + trustedCluster, err := r.p.Client.GetTrustedCluster(ctx, req.ID) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading TrustedCluster", trace.Wrap(err), "trusted_cluster")) return } - trustedCluster := trustedClusterI.(*apitypes.TrustedClusterV2) + + trustedClusterResource := trustedCluster.(*apitypes.TrustedClusterV2) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportTrustedCluster) ImportState(ctx context.Context, req tfs return } - diags = tfschema.CopyTrustedClusterV2ToTerraform(ctx, trustedCluster, &state) + diags = tfschema.CopyTrustedClusterV2ToTerraform(ctx, trustedClusterResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := trustedClusterResource.GetName() - state.Attrs["id"] = types.String{Value: trustedCluster.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_user.go b/terraform/provider/resource_teleport_user.go index b8ff360b0..ab68cd350 100755 --- a/terraform/provider/resource_teleport_user.go +++ b/terraform/provider/resource_teleport_user.go @@ -55,6 +55,7 @@ func (r resourceTeleportUserType) NewResource(_ context.Context, p tfsdk.Provide // Create creates the User func (r resourceTeleportUser) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { + var err error if !r.p.IsConfigured(resp.Diagnostics) { return } @@ -74,13 +75,15 @@ func (r resourceTeleportUser) Create(ctx context.Context, req tfsdk.CreateResour } + userResource := user - _, err := r.p.Client.GetUser(user.Metadata.Name, false) + id := userResource.Metadata.Name + + _, err = r.p.Client.GetUser(id, false) if !trace.IsNotFound(err) { if err == nil { - n := user.Metadata.Name existErr := fmt.Sprintf("User exists in Teleport. Either remove it (tctl rm user/%v)"+ - " or import it to the existing state (terraform import teleport_user.%v %v)", n, n, n) + " or import it to the existing state (terraform import teleport_user.%v %v)", id, id, id) resp.Diagnostics.Append(diagFromErr("User exists in Teleport", trace.Errorf(existErr))) return @@ -90,21 +93,20 @@ func (r resourceTeleportUser) Create(ctx context.Context, req tfsdk.CreateResour return } - err = user.CheckAndSetDefaults() + err = userResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting User defaults", trace.Wrap(err), "user")) return } - err = r.p.Client.CreateUser(ctx, user) + err = r.p.Client.CreateUser(ctx, userResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating User", trace.Wrap(err), "user")) return } - - id := user.Metadata.Name + + // Not really an inferface, just using the same name for easier templating. var userI apitypes.User - tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { @@ -130,11 +132,12 @@ func (r resourceTeleportUser) Create(ctx context.Context, req tfsdk.CreateResour return } - user, ok := userI.(*apitypes.UserV2) + userResource, ok := userI.(*apitypes.UserV2) if !ok { resp.Diagnostics.Append(diagFromWrappedErr("Error reading User", trace.Errorf("Can not convert %T to UserV2", userI), "user")) return } + user = userResource diags = tfschema.CopyUserV2ToTerraform(ctx, user, &plan) resp.Diagnostics.Append(diags...) @@ -177,7 +180,7 @@ func (r resourceTeleportUser) Read(ctx context.Context, req tfsdk.ReadResourceRe resp.Diagnostics.Append(diagFromWrappedErr("Error reading User", trace.Wrap(err), "user")) return } - + user := userI.(*apitypes.UserV2) diags = tfschema.CopyUserV2ToTerraform(ctx, user, &state) resp.Diagnostics.Append(diags...) @@ -212,14 +215,14 @@ func (r resourceTeleportUser) Update(ctx context.Context, req tfsdk.UpdateResour if resp.Diagnostics.HasError() { return } + userResource := user - name := user.Metadata.Name - err := user.CheckAndSetDefaults() - if err != nil { + if err := userResource.CheckAndSetDefaults(); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating User", err, "user")) return } + name := userResource.Metadata.Name userBefore, err := r.p.Client.GetUser(name, false) if err != nil { @@ -227,12 +230,13 @@ func (r resourceTeleportUser) Update(ctx context.Context, req tfsdk.UpdateResour return } - err = r.p.Client.UpdateUser(ctx, user) + err = r.p.Client.UpdateUser(ctx, userResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error updating User", err, "user")) return } - + + // Not really an inferface, just using the same name for easier templating. var userI apitypes.User tries := 0 @@ -259,7 +263,11 @@ func (r resourceTeleportUser) Update(ctx context.Context, req tfsdk.UpdateResour } } - user = userI.(*apitypes.UserV2) + userResource, ok := userI.(*apitypes.UserV2) + if !ok { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading User", trace.Errorf("Can not convert %T to UserV2", userI), "user")) + return + } diags = tfschema.CopyUserV2ToTerraform(ctx, user, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -293,13 +301,14 @@ func (r resourceTeleportUser) Delete(ctx context.Context, req tfsdk.DeleteResour // ImportState imports User state func (r resourceTeleportUser) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - userI, err := r.p.Client.GetUser(req.ID, false) + user, err := r.p.Client.GetUser(req.ID, false) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading User", trace.Wrap(err), "user")) return } - user := userI.(*apitypes.UserV2) + + userResource := user.(*apitypes.UserV2) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportUser) ImportState(ctx context.Context, req tfsdk.ImportR return } - diags = tfschema.CopyUserV2ToTerraform(ctx, user, &state) + diags = tfschema.CopyUserV2ToTerraform(ctx, userResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := userResource.GetName() - state.Attrs["id"] = types.String{Value: user.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/reference.mdx b/terraform/reference.mdx index 83d6d9bbb..60134ba70 100755 --- a/terraform/reference.mdx +++ b/terraform/reference.mdx @@ -7,6 +7,7 @@ description: Terraform provider resources reference Supported resources: +- [teleport_access_list](#teleport_access_list) - [teleport_app](#teleport_app) - [teleport_auth_preference](#teleport_auth_preference) - [teleport_bot](#teleport_bot) @@ -83,6 +84,165 @@ provider "teleport" { } ``` +## teleport_access_list + +| Name | Type | Required | Description | +|--------|--------|----------|------------------------------------------------| +| header | object | | header is the header for the resource. | +| spec | object | | spec is the specification for the access list. | + +### header + +header is the header for the resource. + +| Name | Type | Required | Description | +|----------|--------|----------|--------------------------------------------------------------------| +| kind | string | | kind is a resource kind. | +| metadata | object | | metadata is resource metadata. | +| sub_kind | string | | sub_kind is an optional resource sub kind, used in some resources. | +| version | string | | version is version. | + +#### header.metadata + +metadata is resource metadata. + +| Name | Type | Required | Description | +|-------------|----------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| description | string | | description is object description. | +| expires | RFC3339 time | | | +| labels | map of strings | | labels is a set of labels. | +| name | string | * | name is an object name. | +| namespace | string | | namespace is object namespace. The field should be called "namespace" when it returns in Teleport 2.4. | +| revision | string | | revision is an opaque identifier which tracks the versions of a resource over time. Clients should ignore and not alter its value but must return the revision in any updates of a resource. | + +### spec + +spec is the specification for the access list. + +| Name | Type | Required | Description | +|---------------------|--------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| audit | object | | audit describes the frequency that this access list must be audited. | +| description | string | | description is an optional plaintext description of the access list. | +| grants | object | * | grants describes the access granted by membership to this access list. | +| membership_requires | object | * | membership_requires describes the requirements for a user to be a member of the access list. For a membership to an access list to be effective, the user must meet the requirements of Membership_requires and must be in the members list. | +| owners | object | * | owners is a list of owners of the access list. | +| ownership_requires | object | * | ownership_requires describes the requirements for a user to be an owner of the access list. For ownership of an access list to be effective, the user must meet the requirements of ownership_requires and must be in the owners list. | +| title | string | | title is a plaintext short description of the access list. | + +#### spec.audit + +audit describes the frequency that this access list must be audited. + +| Name | Type | Required | Description | +|-----------------|--------------|----------|-------------| +| frequency | duration | | | +| next_audit_date | RFC3339 time | | | + +#### spec.grants + +grants describes the access granted by membership to this access list. + +| Name | Type | Required | Description | +|--------|------------------|----------|-------------------------------------------------------------------------------------| +| roles | array of strings | | roles are the roles that are granted to users who are members of the access list. | +| traits | object | | traits are the traits that are granted to users who are members of the access list. | + +##### spec.grants.traits + +traits are the traits that are granted to users who are members of the access list. + +| Name | Type | Required | Description | +|--------|------------------|----------|-------------------------------------| +| key | string | | key is the name of the trait. | +| values | array of strings | | values is the list of trait values. | + +#### spec.membership_requires + +membership_requires describes the requirements for a user to be a member of the access list. For a membership to an access list to be effective, the user must meet the requirements of Membership_requires and must be in the members list. + +| Name | Type | Required | Description | +|--------|------------------|----------|------------------------------------------------------------------------------| +| roles | array of strings | | roles are the user roles that must be present for the user to obtain access. | +| traits | object | | traits are the traits that must be present for the user to obtain access. | + +##### spec.membership_requires.traits + +traits are the traits that must be present for the user to obtain access. + +| Name | Type | Required | Description | +|--------|------------------|----------|-------------------------------------| +| key | string | | key is the name of the trait. | +| values | array of strings | | values is the list of trait values. | + +#### spec.owners + +owners is a list of owners of the access list. + +| Name | Type | Required | Description | +|-------------|--------|----------|----------------------------------------------------------------------------------| +| description | string | | description is the plaintext description of the owner and why they are an owner. | +| name | string | | name is the username of the owner. | + +#### spec.ownership_requires + +ownership_requires describes the requirements for a user to be an owner of the access list. For ownership of an access list to be effective, the user must meet the requirements of ownership_requires and must be in the owners list. + +| Name | Type | Required | Description | +|--------|------------------|----------|------------------------------------------------------------------------------| +| roles | array of strings | | roles are the user roles that must be present for the user to obtain access. | +| traits | object | | traits are the traits that must be present for the user to obtain access. | + +##### spec.ownership_requires.traits + +traits are the traits that must be present for the user to obtain access. + +| Name | Type | Required | Description | +|--------|------------------|----------|-------------------------------------| +| key | string | | key is the name of the trait. | +| values | array of strings | | values is the list of trait values. | + +Example: + +``` +resource "teleport_access_list" "crane-operation" { + header = { + metadata = { + name = "crane-operation" + labels = { + example = "yes" + } + } + } + spec = { + description = "Used to grant access to the crane." + owners = [ + { + name = "gru" + description = "The supervillain." + } + ] + membership_requires = { + roles = ["minion"] + } + ownership_requires = { + roles = ["supervillain"] + } + grants = { + roles = ["crane-operator"] + traits = [{ + key = "allowed-machines" + values = ["crane", "forklift"] + }] + } + title = "Crane operation" + audit = { + frequency = "3600h" // 150 days + } + } +} + +``` + ## teleport_app | Name | Type | Required | Description | diff --git a/terraform/test/access_list_test.go b/terraform/test/access_list_test.go new file mode 100644 index 000000000..fa8861aa5 --- /dev/null +++ b/terraform/test/access_list_test.go @@ -0,0 +1,81 @@ +/* +Copyright 2015-2021 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package test + +import ( + "context" + + "github.com/gravitational/trace" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func (s *TerraformSuite) TestAccessList() { + checkAccessListDestroyed := func(state *terraform.State) error { + _, err := s.client.AccessListClient().GetAccessList(context.TODO(), "test") + if trace.IsNotFound(err) { + return nil + } + + return err + } + + name := "teleport_access_list.test" + + resource.Test(s.T(), resource.TestCase{ + ProtoV6ProviderFactories: s.terraformProviders, + CheckDestroy: checkAccessListDestroyed, + Steps: []resource.TestStep{ + { + Config: s.getFixture("access_list_0_create.tf"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "header.metadata.name", "test"), + resource.TestCheckResourceAttr(name, "spec.description", "test description"), + resource.TestCheckResourceAttr(name, "spec.owners.0.name", "gru"), + resource.TestCheckResourceAttr(name, "spec.membership_requires.roles.0", "minion"), + resource.TestCheckResourceAttr(name, "spec.grants.roles.0", "crane-operator"), + ), + }, + { + Config: s.getFixture("access_list_0_create.tf"), + PlanOnly: true, + }, + { + Config: s.getFixture("access_list_1_update.tf"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "spec.grants.traits.0.key", "allowed-machines"), + resource.TestCheckResourceAttr(name, "spec.grants.traits.0.values.0", "crane"), + resource.TestCheckResourceAttr(name, "spec.grants.traits.0.values.1", "forklift"), + ), + }, + { + Config: s.getFixture("access_list_1_update.tf"), + PlanOnly: true, + }, + { + Config: s.getFixture("access_list_2_expiring.tf"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "header.metadata.expires", "2038-01-01T00:00:00Z"), + ), + }, + { + Config: s.getFixture("access_list_2_expiring.tf"), + PlanOnly: true, + }, + }, + }) +} diff --git a/terraform/test/fixtures/access_list_0_create.tf b/terraform/test/fixtures/access_list_0_create.tf new file mode 100644 index 000000000..53b3fe9fa --- /dev/null +++ b/terraform/test/fixtures/access_list_0_create.tf @@ -0,0 +1,32 @@ +resource "teleport_access_list" "test" { + header = { + metadata = { + name = "test" + labels = { + example = "yes" + } + } + } + spec = { + description = "test description" + owners = [ + { + name = "gru" + description = "The supervillain." + } + ] + membership_requires = { + roles = ["minion"] + } + ownership_requires = { + roles = ["supervillain"] + } + grants = { + roles = ["crane-operator"] + } + title = "Hello" + audit = { + frequency = "3600h" + } + } +} diff --git a/terraform/test/fixtures/access_list_1_update.tf b/terraform/test/fixtures/access_list_1_update.tf new file mode 100644 index 000000000..5af8a105a --- /dev/null +++ b/terraform/test/fixtures/access_list_1_update.tf @@ -0,0 +1,36 @@ +resource "teleport_access_list" "test" { + header = { + metadata = { + name = "test" + labels = { + example = "yes" + } + } + } + spec = { + description = "test description" + owners = [ + { + name = "gru" + description = "The supervillain." + } + ] + membership_requires = { + roles = ["minion"] + } + ownership_requires = { + roles = ["supervillain"] + } + grants = { + roles = ["crane-operator"] + traits = [{ + key = "allowed-machines" + values = ["crane", "forklift"] + }] + } + title = "Hello" + audit = { + frequency = "7200h" + } + } +} diff --git a/terraform/test/fixtures/access_list_2_expiring.tf b/terraform/test/fixtures/access_list_2_expiring.tf new file mode 100644 index 000000000..8cbae6682 --- /dev/null +++ b/terraform/test/fixtures/access_list_2_expiring.tf @@ -0,0 +1,37 @@ +resource "teleport_access_list" "test" { + header = { + metadata = { + name = "test" + labels = { + example = "yes" + } + expires = "2038-01-01T00:00:00Z" + } + } + spec = { + description = "test description" + owners = [ + { + name = "gru" + description = "The supervillain." + } + ] + membership_requires = { + roles = ["minion"] + } + ownership_requires = { + roles = ["supervillain"] + } + grants = { + roles = ["crane-operator"] + traits = [{ + key = "allowed-machines" + values = ["crane", "forklift"] + }] + } + title = "Hello" + audit = { + frequency = "7200h" + } + } +} diff --git a/terraform/test/main_test.go b/terraform/test/main_test.go index 17e1bf91f..f797cdac3 100644 --- a/terraform/test/main_test.go +++ b/terraform/test/main_test.go @@ -130,6 +130,7 @@ func (s *TerraformBaseSuite) SetupSuite() { types.NewRule("saml", unrestricted), types.NewRule("login_rule", unrestricted), types.NewRule("device", unrestricted), + types.NewRule("access_list", unrestricted), }, Logins: []string{me.Username}, }, diff --git a/terraform/tfschema/accesslist/v1/accesslist_terraform.go b/terraform/tfschema/accesslist/v1/accesslist_terraform.go new file mode 100644 index 000000000..e54ab3529 --- /dev/null +++ b/terraform/tfschema/accesslist/v1/accesslist_terraform.go @@ -0,0 +1,2208 @@ +/* +Copyright 2015-2022 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: teleport/accesslist/v1/accesslist.proto + +package v1 + +import ( + context "context" + fmt "fmt" + math "math" + + proto "github.com/gogo/protobuf/proto" + github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1" + _ "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + github_com_gravitational_teleport_api_gen_proto_go_teleport_header_v1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + _ "github.com/gravitational/teleport/api/gen/proto/go/teleport/trait/v1" + github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/trait/v1" + github_com_hashicorp_terraform_plugin_framework_attr "github.com/hashicorp/terraform-plugin-framework/attr" + github_com_hashicorp_terraform_plugin_framework_diag "github.com/hashicorp/terraform-plugin-framework/diag" + github_com_hashicorp_terraform_plugin_framework_tfsdk "github.com/hashicorp/terraform-plugin-framework/tfsdk" + github_com_hashicorp_terraform_plugin_framework_types "github.com/hashicorp/terraform-plugin-framework/types" + github_com_hashicorp_terraform_plugin_go_tftypes "github.com/hashicorp/terraform-plugin-go/tftypes" + _ "google.golang.org/protobuf/types/known/durationpb" + _ "google.golang.org/protobuf/types/known/timestamppb" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// GenSchemaAccessList returns tfsdk.Schema definition for AccessList +func GenSchemaAccessList(ctx context.Context) (github_com_hashicorp_terraform_plugin_framework_tfsdk.Schema, github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics) { + return github_com_hashicorp_terraform_plugin_framework_tfsdk.Schema{Attributes: map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "header": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "kind": { + Description: "kind is a resource kind.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "metadata": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "description is object description.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "expires": GenSchemaTimestamp(ctx), + "labels": { + Description: "labels is a set of labels.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.MapType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }, + "name": { + Description: "name is an object name.", + PlanModifiers: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributePlanModifier{github_com_hashicorp_terraform_plugin_framework_tfsdk.RequiresReplace()}, + Required: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "namespace": { + Computed: true, + Description: "namespace is object namespace. The field should be called \"namespace\" when it returns in Teleport 2.4.", + Optional: true, + PlanModifiers: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributePlanModifier{github_com_hashicorp_terraform_plugin_framework_tfsdk.UseStateForUnknown()}, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "revision": { + Description: "revision is an opaque identifier which tracks the versions of a resource over time. Clients should ignore and not alter its value but must return the revision in any updates of a resource.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "metadata is resource metadata.", + Optional: true, + }, + "sub_kind": { + Description: "sub_kind is an optional resource sub kind, used in some resources.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "version": { + Description: "version is version.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "header is the header for the resource.", + Optional: true, + }, + "id": { + Computed: true, + Optional: false, + PlanModifiers: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributePlanModifier{github_com_hashicorp_terraform_plugin_framework_tfsdk.UseStateForUnknown()}, + Required: false, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "spec": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "audit": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "frequency": GenSchemaDuration(ctx), + "next_audit_date": GenSchemaTimestamp(ctx), + }), + Description: "audit describes the frequency that this access list must be audited.", + Optional: true, + }, + "description": { + Description: "description is an optional plaintext description of the access list.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "grants": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "roles": { + Description: "roles are the roles that are granted to users who are members of the access list.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.ListType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }, + "traits": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.ListNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "key": { + Description: "key is the name of the trait.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "values": { + Description: "values is the list of trait values.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.ListType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }, + }), + Description: "traits are the traits that are granted to users who are members of the access list.", + Optional: true, + }, + }), + Description: "grants describes the access granted by membership to this access list.", + Required: true, + }, + "membership_requires": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "roles": { + Description: "roles are the user roles that must be present for the user to obtain access.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.ListType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }, + "traits": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.ListNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "key": { + Description: "key is the name of the trait.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "values": { + Description: "values is the list of trait values.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.ListType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }, + }), + Description: "traits are the traits that must be present for the user to obtain access.", + Optional: true, + }, + }), + Description: "membership_requires describes the requirements for a user to be a member of the access list. For a membership to an access list to be effective, the user must meet the requirements of Membership_requires and must be in the members list.", + Required: true, + }, + "owners": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.ListNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "description is the plaintext description of the owner and why they are an owner.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "name": { + Description: "name is the username of the owner.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "owners is a list of owners of the access list.", + Required: true, + }, + "ownership_requires": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "roles": { + Description: "roles are the user roles that must be present for the user to obtain access.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.ListType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }, + "traits": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.ListNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "key": { + Description: "key is the name of the trait.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "values": { + Description: "values is the list of trait values.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.ListType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }, + }), + Description: "traits are the traits that must be present for the user to obtain access.", + Optional: true, + }, + }), + Description: "ownership_requires describes the requirements for a user to be an owner of the access list. For ownership of an access list to be effective, the user must meet the requirements of ownership_requires and must be in the owners list.", + Required: true, + }, + "title": { + Description: "title is a plaintext short description of the access list.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "spec is the specification for the access list.", + Optional: true, + }, + }}, nil +} + +// CopyAccessListFromTerraform copies contents of the source Terraform object into a target struct +func CopyAccessListFromTerraform(_ context.Context, tf github_com_hashicorp_terraform_plugin_framework_types.Object, obj *github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessList) github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics { + var diags github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics + { + a, ok := tf.Attrs["header"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Header = nil + if !v.Null && !v.Unknown { + tf := v + obj.Header = &github_com_gravitational_teleport_api_gen_proto_go_teleport_header_v1.ResourceHeader{} + obj := obj.Header + { + a, ok := tf.Attrs["kind"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.kind"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.kind", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Kind = t + } + } + } + { + a, ok := tf.Attrs["sub_kind"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.sub_kind"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.sub_kind", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.SubKind = t + } + } + } + { + a, ok := tf.Attrs["version"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.version"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.version", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Version = t + } + } + } + { + a, ok := tf.Attrs["metadata"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.metadata"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.metadata", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Metadata = nil + if !v.Null && !v.Unknown { + tf := v + obj.Metadata = &github_com_gravitational_teleport_api_gen_proto_go_teleport_header_v1.Metadata{} + obj := obj.Metadata + { + a, ok := tf.Attrs["name"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.metadata.name"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.metadata.name", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Name = t + } + } + } + { + a, ok := tf.Attrs["namespace"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.metadata.namespace"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.metadata.namespace", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Namespace = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.metadata.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.metadata.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["labels"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.metadata.labels"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Map) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.metadata.labels", "github.com/hashicorp/terraform-plugin-framework/types.Map"}) + } else { + obj.Labels = make(map[string]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.metadata.labels", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Labels[k] = t + } + } + } + } + } + } + { + a, ok := tf.Attrs["expires"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.metadata.expires"}) + } + CopyFromTimestamp(diags, a, &obj.Expires) + } + { + a, ok := tf.Attrs["revision"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.metadata.revision"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.metadata.revision", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Revision = t + } + } + } + } + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["spec"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Spec = nil + if !v.Null && !v.Unknown { + tf := v + obj.Spec = &github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessListSpec{} + obj := obj.Spec + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["owners"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.owners"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.owners", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Owners = make([]*github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessListOwner, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.owners", "github_com_hashicorp_terraform_plugin_framework_types.Object"}) + } else { + var t *github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessListOwner + if !v.Null && !v.Unknown { + tf := v + t = &github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessListOwner{} + obj := t + { + a, ok := tf.Attrs["name"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.owners.name"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.owners.name", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Name = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.owners.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.owners.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + } + obj.Owners[k] = t + } + } + } + } + } + } + { + a, ok := tf.Attrs["audit"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.audit"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.audit", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Audit = nil + if !v.Null && !v.Unknown { + tf := v + obj.Audit = &github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessListAudit{} + obj := obj.Audit + { + a, ok := tf.Attrs["frequency"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.audit.frequency"}) + } + CopyFromDuration(diags, a, &obj.Frequency) + } + { + a, ok := tf.Attrs["next_audit_date"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.audit.next_audit_date"}) + } + CopyFromTimestamp(diags, a, &obj.NextAuditDate) + } + } + } + } + } + { + a, ok := tf.Attrs["membership_requires"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.membership_requires"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.membership_requires", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.MembershipRequires = nil + if !v.Null && !v.Unknown { + tf := v + obj.MembershipRequires = &github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessListRequires{} + obj := obj.MembershipRequires + { + a, ok := tf.Attrs["roles"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.membership_requires.roles"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.membership_requires.roles", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Roles = make([]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.membership_requires.roles", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Roles[k] = t + } + } + } + } + } + } + { + a, ok := tf.Attrs["traits"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.membership_requires.traits"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.membership_requires.traits", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Traits = make([]*github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.membership_requires.traits", "github_com_hashicorp_terraform_plugin_framework_types.Object"}) + } else { + var t *github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait + if !v.Null && !v.Unknown { + tf := v + t = &github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait{} + obj := t + { + a, ok := tf.Attrs["key"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.membership_requires.traits.key"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.membership_requires.traits.key", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Key = t + } + } + } + { + a, ok := tf.Attrs["values"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.membership_requires.traits.values"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.membership_requires.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Values = make([]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.membership_requires.traits.values", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Values[k] = t + } + } + } + } + } + } + } + obj.Traits[k] = t + } + } + } + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["ownership_requires"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.ownership_requires"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.ownership_requires", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.OwnershipRequires = nil + if !v.Null && !v.Unknown { + tf := v + obj.OwnershipRequires = &github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessListRequires{} + obj := obj.OwnershipRequires + { + a, ok := tf.Attrs["roles"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.ownership_requires.roles"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.ownership_requires.roles", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Roles = make([]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.ownership_requires.roles", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Roles[k] = t + } + } + } + } + } + } + { + a, ok := tf.Attrs["traits"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.ownership_requires.traits"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.ownership_requires.traits", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Traits = make([]*github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.ownership_requires.traits", "github_com_hashicorp_terraform_plugin_framework_types.Object"}) + } else { + var t *github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait + if !v.Null && !v.Unknown { + tf := v + t = &github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait{} + obj := t + { + a, ok := tf.Attrs["key"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.ownership_requires.traits.key"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.ownership_requires.traits.key", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Key = t + } + } + } + { + a, ok := tf.Attrs["values"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.ownership_requires.traits.values"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.ownership_requires.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Values = make([]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.ownership_requires.traits.values", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Values[k] = t + } + } + } + } + } + } + } + obj.Traits[k] = t + } + } + } + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["grants"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.grants"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.grants", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Grants = nil + if !v.Null && !v.Unknown { + tf := v + obj.Grants = &github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessListGrants{} + obj := obj.Grants + { + a, ok := tf.Attrs["roles"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.grants.roles"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.grants.roles", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Roles = make([]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.grants.roles", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Roles[k] = t + } + } + } + } + } + } + { + a, ok := tf.Attrs["traits"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.grants.traits"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.grants.traits", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Traits = make([]*github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.grants.traits", "github_com_hashicorp_terraform_plugin_framework_types.Object"}) + } else { + var t *github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait + if !v.Null && !v.Unknown { + tf := v + t = &github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait{} + obj := t + { + a, ok := tf.Attrs["key"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.grants.traits.key"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.grants.traits.key", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Key = t + } + } + } + { + a, ok := tf.Attrs["values"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.grants.traits.values"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.grants.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Values = make([]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.grants.traits.values", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Values[k] = t + } + } + } + } + } + } + } + obj.Traits[k] = t + } + } + } + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + return diags +} + +// CopyAccessListToTerraform copies contents of the source Terraform object into a target struct +func CopyAccessListToTerraform(ctx context.Context, obj *github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessList, tf *github_com_hashicorp_terraform_plugin_framework_types.Object) github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics { + var diags github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics + tf.Null = false + tf.Unknown = false + if tf.Attrs == nil { + tf.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value) + } + { + a, ok := tf.AttrTypes["header"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["header"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Header == nil { + v.Null = true + } else { + obj := obj.Header + tf := &v + { + t, ok := tf.AttrTypes["kind"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.kind"}) + } else { + v, ok := tf.Attrs["kind"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.header.kind", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.kind", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Kind) == "" + } + v.Value = string(obj.Kind) + v.Unknown = false + tf.Attrs["kind"] = v + } + } + { + t, ok := tf.AttrTypes["sub_kind"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.sub_kind"}) + } else { + v, ok := tf.Attrs["sub_kind"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.header.sub_kind", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.sub_kind", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.SubKind) == "" + } + v.Value = string(obj.SubKind) + v.Unknown = false + tf.Attrs["sub_kind"] = v + } + } + { + t, ok := tf.AttrTypes["version"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.version"}) + } else { + v, ok := tf.Attrs["version"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.header.version", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.version", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Version) == "" + } + v.Value = string(obj.Version) + v.Unknown = false + tf.Attrs["version"] = v + } + } + { + a, ok := tf.AttrTypes["metadata"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.metadata"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.metadata", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["metadata"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Metadata == nil { + v.Null = true + } else { + obj := obj.Metadata + tf := &v + { + t, ok := tf.AttrTypes["name"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.metadata.name"}) + } else { + v, ok := tf.Attrs["name"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.header.metadata.name", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.metadata.name", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Name) == "" + } + v.Value = string(obj.Name) + v.Unknown = false + tf.Attrs["name"] = v + } + } + { + t, ok := tf.AttrTypes["namespace"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.metadata.namespace"}) + } else { + v, ok := tf.Attrs["namespace"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.header.metadata.namespace", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.metadata.namespace", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Namespace) == "" + } + v.Value = string(obj.Namespace) + v.Unknown = false + tf.Attrs["namespace"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.metadata.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.header.metadata.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.metadata.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + a, ok := tf.AttrTypes["labels"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.metadata.labels"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.MapType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.metadata.labels", "github.com/hashicorp/terraform-plugin-framework/types.MapType"}) + } else { + c, ok := tf.Attrs["labels"].(github_com_hashicorp_terraform_plugin_framework_types.Map) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.Map{ + + ElemType: o.ElemType, + Elems: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Labels)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Labels)) + } + } + if obj.Labels != nil { + t := o.ElemType + for k, a := range obj.Labels { + v, ok := tf.Attrs["labels"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.header.metadata.labels", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.metadata.labels", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = false + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Labels) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["labels"] = c + } + } + } + { + t, ok := tf.AttrTypes["expires"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.metadata.expires"}) + } else { + v := CopyToTimestamp(diags, obj.Expires, t, tf.Attrs["expires"]) + tf.Attrs["expires"] = v + } + } + { + t, ok := tf.AttrTypes["revision"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.metadata.revision"}) + } else { + v, ok := tf.Attrs["revision"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.header.metadata.revision", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.metadata.revision", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Revision) == "" + } + v.Value = string(obj.Revision) + v.Unknown = false + tf.Attrs["revision"] = v + } + } + } + v.Unknown = false + tf.Attrs["metadata"] = v + } + } + } + } + v.Unknown = false + tf.Attrs["header"] = v + } + } + } + { + a, ok := tf.AttrTypes["spec"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["spec"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Spec == nil { + v.Null = true + } else { + obj := obj.Spec + tf := &v + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + a, ok := tf.AttrTypes["owners"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.owners"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.owners", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["owners"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Owners)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Owners)) + } + } + if obj.Owners != nil { + o := o.ElemType.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if len(obj.Owners) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Owners)) + } + for k, a := range obj.Owners { + v, ok := tf.Attrs["owners"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if a == nil { + v.Null = true + } else { + obj := a + tf := &v + { + t, ok := tf.AttrTypes["name"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.owners.name"}) + } else { + v, ok := tf.Attrs["name"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.owners.name", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.owners.name", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Name) == "" + } + v.Value = string(obj.Name) + v.Unknown = false + tf.Attrs["name"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.owners.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.owners.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.owners.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + } + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Owners) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["owners"] = c + } + } + } + { + a, ok := tf.AttrTypes["audit"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.audit"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.audit", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["audit"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Audit == nil { + v.Null = true + } else { + obj := obj.Audit + tf := &v + { + t, ok := tf.AttrTypes["frequency"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.audit.frequency"}) + } else { + v := CopyToDuration(diags, obj.Frequency, t, tf.Attrs["frequency"]) + tf.Attrs["frequency"] = v + } + } + { + t, ok := tf.AttrTypes["next_audit_date"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.audit.next_audit_date"}) + } else { + v := CopyToTimestamp(diags, obj.NextAuditDate, t, tf.Attrs["next_audit_date"]) + tf.Attrs["next_audit_date"] = v + } + } + } + v.Unknown = false + tf.Attrs["audit"] = v + } + } + } + { + a, ok := tf.AttrTypes["membership_requires"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.membership_requires"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.membership_requires", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["membership_requires"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.MembershipRequires == nil { + v.Null = true + } else { + obj := obj.MembershipRequires + tf := &v + { + a, ok := tf.AttrTypes["roles"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.membership_requires.roles"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.membership_requires.roles", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["roles"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)) + } + } + if obj.Roles != nil { + t := o.ElemType + if len(obj.Roles) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)) + } + for k, a := range obj.Roles { + v, ok := tf.Attrs["roles"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.membership_requires.roles", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.membership_requires.roles", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(a) == "" + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Roles) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["roles"] = c + } + } + } + { + a, ok := tf.AttrTypes["traits"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.membership_requires.traits"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.membership_requires.traits", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["traits"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)) + } + } + if obj.Traits != nil { + o := o.ElemType.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if len(obj.Traits) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)) + } + for k, a := range obj.Traits { + v, ok := tf.Attrs["traits"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if a == nil { + v.Null = true + } else { + obj := a + tf := &v + { + t, ok := tf.AttrTypes["key"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.membership_requires.traits.key"}) + } else { + v, ok := tf.Attrs["key"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.membership_requires.traits.key", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.membership_requires.traits.key", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Key) == "" + } + v.Value = string(obj.Key) + v.Unknown = false + tf.Attrs["key"] = v + } + } + { + a, ok := tf.AttrTypes["values"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.membership_requires.traits.values"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.membership_requires.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["values"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)) + } + } + if obj.Values != nil { + t := o.ElemType + if len(obj.Values) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)) + } + for k, a := range obj.Values { + v, ok := tf.Attrs["values"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.membership_requires.traits.values", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.membership_requires.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(a) == "" + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Values) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["values"] = c + } + } + } + } + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Traits) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["traits"] = c + } + } + } + } + v.Unknown = false + tf.Attrs["membership_requires"] = v + } + } + } + { + a, ok := tf.AttrTypes["ownership_requires"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.ownership_requires"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.ownership_requires", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["ownership_requires"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.OwnershipRequires == nil { + v.Null = true + } else { + obj := obj.OwnershipRequires + tf := &v + { + a, ok := tf.AttrTypes["roles"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.ownership_requires.roles"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.ownership_requires.roles", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["roles"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)) + } + } + if obj.Roles != nil { + t := o.ElemType + if len(obj.Roles) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)) + } + for k, a := range obj.Roles { + v, ok := tf.Attrs["roles"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.ownership_requires.roles", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.ownership_requires.roles", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(a) == "" + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Roles) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["roles"] = c + } + } + } + { + a, ok := tf.AttrTypes["traits"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.ownership_requires.traits"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.ownership_requires.traits", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["traits"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)) + } + } + if obj.Traits != nil { + o := o.ElemType.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if len(obj.Traits) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)) + } + for k, a := range obj.Traits { + v, ok := tf.Attrs["traits"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if a == nil { + v.Null = true + } else { + obj := a + tf := &v + { + t, ok := tf.AttrTypes["key"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.ownership_requires.traits.key"}) + } else { + v, ok := tf.Attrs["key"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.ownership_requires.traits.key", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.ownership_requires.traits.key", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Key) == "" + } + v.Value = string(obj.Key) + v.Unknown = false + tf.Attrs["key"] = v + } + } + { + a, ok := tf.AttrTypes["values"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.ownership_requires.traits.values"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.ownership_requires.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["values"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)) + } + } + if obj.Values != nil { + t := o.ElemType + if len(obj.Values) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)) + } + for k, a := range obj.Values { + v, ok := tf.Attrs["values"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.ownership_requires.traits.values", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.ownership_requires.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(a) == "" + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Values) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["values"] = c + } + } + } + } + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Traits) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["traits"] = c + } + } + } + } + v.Unknown = false + tf.Attrs["ownership_requires"] = v + } + } + } + { + a, ok := tf.AttrTypes["grants"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.grants"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.grants", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["grants"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Grants == nil { + v.Null = true + } else { + obj := obj.Grants + tf := &v + { + a, ok := tf.AttrTypes["roles"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.grants.roles"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.grants.roles", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["roles"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)) + } + } + if obj.Roles != nil { + t := o.ElemType + if len(obj.Roles) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)) + } + for k, a := range obj.Roles { + v, ok := tf.Attrs["roles"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.grants.roles", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.grants.roles", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(a) == "" + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Roles) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["roles"] = c + } + } + } + { + a, ok := tf.AttrTypes["traits"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.grants.traits"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.grants.traits", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["traits"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)) + } + } + if obj.Traits != nil { + o := o.ElemType.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if len(obj.Traits) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)) + } + for k, a := range obj.Traits { + v, ok := tf.Attrs["traits"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if a == nil { + v.Null = true + } else { + obj := a + tf := &v + { + t, ok := tf.AttrTypes["key"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.grants.traits.key"}) + } else { + v, ok := tf.Attrs["key"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.grants.traits.key", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.grants.traits.key", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Key) == "" + } + v.Value = string(obj.Key) + v.Unknown = false + tf.Attrs["key"] = v + } + } + { + a, ok := tf.AttrTypes["values"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.grants.traits.values"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.grants.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["values"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)) + } + } + if obj.Values != nil { + t := o.ElemType + if len(obj.Values) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)) + } + for k, a := range obj.Values { + v, ok := tf.Attrs["values"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.grants.traits.values", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.grants.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(a) == "" + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Values) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["values"] = c + } + } + } + } + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Traits) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["traits"] = c + } + } + } + } + v.Unknown = false + tf.Attrs["grants"] = v + } + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["spec"] = v + } + } + } + return diags +} + +// attrReadMissingDiag represents diagnostic message on an attribute missing in the source object +type attrReadMissingDiag struct { + Path string +} + +func (d attrReadMissingDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrReadMissingDiag) Summary() string { + return "Error reading from Terraform object" +} + +func (d attrReadMissingDiag) Detail() string { + return fmt.Sprintf("A value for %v is missing in the source Terraform object Attrs", d.Path) +} + +func (d attrReadMissingDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrReadConversionFailureDiag represents diagnostic message on a failed type conversion on read +type attrReadConversionFailureDiag struct { + Path string + Type string +} + +func (d attrReadConversionFailureDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrReadConversionFailureDiag) Summary() string { + return "Error reading from Terraform object" +} + +func (d attrReadConversionFailureDiag) Detail() string { + return fmt.Sprintf("A value for %v can not be converted to %v", d.Path, d.Type) +} + +func (d attrReadConversionFailureDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrWriteMissingDiag represents diagnostic message on an attribute missing in the target object +type attrWriteMissingDiag struct { + Path string +} + +func (d attrWriteMissingDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrWriteMissingDiag) Summary() string { + return "Error writing to Terraform object" +} + +func (d attrWriteMissingDiag) Detail() string { + return fmt.Sprintf("A value for %v is missing in the source Terraform object AttrTypes", d.Path) +} + +func (d attrWriteMissingDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrWriteConversionFailureDiag represents diagnostic message on a failed type conversion on write +type attrWriteConversionFailureDiag struct { + Path string + Type string +} + +func (d attrWriteConversionFailureDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrWriteConversionFailureDiag) Summary() string { + return "Error writing to Terraform object" +} + +func (d attrWriteConversionFailureDiag) Detail() string { + return fmt.Sprintf("A value for %v can not be converted to %v", d.Path, d.Type) +} + +func (d attrWriteConversionFailureDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrWriteGeneralError represents diagnostic message on a generic error on write +type attrWriteGeneralError struct { + Path string + Err error +} + +func (d attrWriteGeneralError) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrWriteGeneralError) Summary() string { + return "Error writing to Terraform object" +} + +func (d attrWriteGeneralError) Detail() string { + return fmt.Sprintf("%s: %s", d.Path, d.Err.Error()) +} + +func (d attrWriteGeneralError) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} diff --git a/terraform/tfschema/accesslist/v1/custom_types.go b/terraform/tfschema/accesslist/v1/custom_types.go new file mode 100644 index 000000000..21b214e75 --- /dev/null +++ b/terraform/tfschema/accesslist/v1/custom_types.go @@ -0,0 +1,100 @@ +/* +Copyright 2023 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/gravitational/teleport-plugins/terraform/tfschema" +) + +func GenSchemaTimestamp(_ context.Context) tfsdk.Attribute { + return tfsdk.Attribute{ + Optional: true, + Type: tfschema.UseRFC3339Time(), + } +} + +func CopyFromTimestamp(diags diag.Diagnostics, v attr.Value, o **timestamppb.Timestamp) { + value, ok := v.(tfschema.TimeValue) + if !ok { + diags.AddError("Error reading from Terraform object", fmt.Sprintf("Can not convert %T to String", v)) + return + } + + if value.IsNull() { + *o = nil + } else { + *o = timestamppb.New(value.Value) + } +} + +func CopyToTimestamp(diags diag.Diagnostics, o *timestamppb.Timestamp, t attr.Type, v attr.Value) attr.Value { + value, ok := v.(tfschema.TimeValue) + if !ok { + value = tfschema.TimeValue{} + } + + if o == nil { + value.Null = true + return value + } + + value.Value = (*o).AsTime() + + return value +} + +func GenSchemaDuration(_ context.Context) tfsdk.Attribute { + return tfsdk.Attribute{ + Optional: true, + Type: tfschema.DurationType{}, + } +} + +func CopyFromDuration(diags diag.Diagnostics, v attr.Value, o **durationpb.Duration) { + value, ok := v.(tfschema.DurationValue) + if !ok { + diags.AddError("Error reading from Terraform object", fmt.Sprintf("Can not convert %T to String", v)) + return + } + + *o = durationpb.New(value.Value) +} + +func CopyToDuration(diags diag.Diagnostics, o *durationpb.Duration, t attr.Type, v attr.Value) attr.Value { + value, ok := v.(tfschema.DurationValue) + if !ok { + value = tfschema.DurationValue{} + } + + if o == nil { + value.Null = true + return value + } + + value.Value = (*o).AsDuration() + + return value +}