mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-21 14:35:22 +08:00
add scoped role assignments to terraform (#65632)
fix template regenerate docs Update provider.go address linter issue lint delete uuid requirement - fix tests Update resource.tf fix tests fix test adjust to use envelope types fix Update presets.go Update assignment_terraform.go make docs
This commit is contained in:
+1
@@ -40,6 +40,7 @@ The Teleport Terraform provider supports the following data-sources:
|
||||
- [`teleport_saml_connector`](./saml_connector.mdx)
|
||||
- [`teleport_saml_idp_service_provider`](./saml_idp_service_provider.mdx)
|
||||
- [`teleport_scoped_role`](./scoped_role.mdx)
|
||||
- [`teleport_scoped_role_assignment`](./scoped_role_assignment.mdx)
|
||||
- [`teleport_scoped_token`](./scoped_token.mdx)
|
||||
- [`teleport_session_recording_config`](./session_recording_config.mdx)
|
||||
- [`teleport_static_host_user`](./static_host_user.mdx)
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
---
|
||||
title: Reference for the teleport_scoped_role_assignment Terraform data-source
|
||||
sidebar_label: scoped_role_assignment
|
||||
description: This page describes the supported values of the teleport_scoped_role_assignment data-source of the Teleport Terraform provider.
|
||||
---
|
||||
|
||||
{/*Auto-generated file. Do not edit.*/}
|
||||
{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/}
|
||||
|
||||
{/* Disable the outdated name check since data source fields occasionally need
|
||||
to refer to these. */}
|
||||
{/* vale 3rd-party-products.former-names = NO */}
|
||||
|
||||
This page describes the supported values of the `teleport_scoped_role_assignment` data source of the
|
||||
Teleport Terraform provider.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{/* schema generated by tfplugindocs */}
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `metadata` (Attributes) Metadata contains the resource metadata. (see [below for nested schema](#nested-schema-for-metadata))
|
||||
- `scope` (String) Scope is the scope of the role assignment resource.
|
||||
- `spec` (Attributes) Spec is the role assignment specification. (see [below for nested schema](#nested-schema-for-spec))
|
||||
- `sub_kind` (String) SubKind is the resource sub-kind.
|
||||
- `version` (String) Version is the resource version.
|
||||
|
||||
### Nested Schema for `metadata`
|
||||
|
||||
Required:
|
||||
|
||||
- `name` (String) name is an object name.
|
||||
|
||||
Optional:
|
||||
|
||||
- `description` (String) description is object description.
|
||||
- `expires` (String) expires is a global expiry time header can be set on any resource in the system.
|
||||
- `labels` (Map of String) labels is a set of labels.
|
||||
|
||||
|
||||
### Nested Schema for `spec`
|
||||
|
||||
Required:
|
||||
|
||||
- `assignments` (Attributes List) Assignments is a list of individual role @ scope assignments. (see [below for nested schema](#nested-schema-for-specassignments))
|
||||
|
||||
Optional:
|
||||
|
||||
- `bot_name` (String) Name of the Bot to whom all contained assignments apply. Mutually exclusive with `user`.
|
||||
- `bot_scope` (String) Scope of the Bot to whom all contained assignments apply. Required if `bot_name` is set. If specified, assignment scopes must be equal or descendent of this scope.
|
||||
- `user` (String) User is the user to whom all contained assignments apply. Mutually exclusive with `bot_name`.
|
||||
|
||||
### Nested Schema for `spec.assignments`
|
||||
|
||||
Optional:
|
||||
|
||||
- `role` (String) Roles is the name of the role that is assigned by this assignment.
|
||||
- `scope` (String) Scope is the scope to which the role is assigned. This must be a member/child of the scope of the [ScopedRoleAssignment] in which this assignment is contained.
|
||||
|
||||
@@ -45,6 +45,7 @@ The Teleport Terraform provider supports the following resources:
|
||||
- [`teleport_saml_connector`](./saml_connector.mdx)
|
||||
- [`teleport_saml_idp_service_provider`](./saml_idp_service_provider.mdx)
|
||||
- [`teleport_scoped_role`](./scoped_role.mdx)
|
||||
- [`teleport_scoped_role_assignment`](./scoped_role_assignment.mdx)
|
||||
- [`teleport_scoped_token`](./scoped_token.mdx)
|
||||
- [`teleport_server`](./server.mdx)
|
||||
- [`teleport_session_recording_config`](./session_recording_config.mdx)
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: Reference for the teleport_scoped_role_assignment Terraform resource
|
||||
sidebar_label: scoped_role_assignment
|
||||
description: This page describes the supported values of the teleport_scoped_role_assignment resource of the Teleport Terraform provider.
|
||||
---
|
||||
|
||||
{/*Auto-generated file. Do not edit.*/}
|
||||
{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/}
|
||||
|
||||
{/* Disable the outdated name check since resource fields occasionally need
|
||||
to refer to these. */}
|
||||
{/* vale 3rd-party-products.former-names = NO */}
|
||||
|
||||
This page describes the supported values of the teleport_scoped_role_assignment resource of the Teleport Terraform provider.
|
||||
|
||||
|
||||
|
||||
|
||||
## Example Usage
|
||||
|
||||
```hcl
|
||||
# Teleport Scoped Role Assignment resource
|
||||
#
|
||||
# Assigns an existing scoped role to a user at a specific scope.
|
||||
# The referenced scoped role must already exist.
|
||||
|
||||
# resource "teleport_scoped_role" "example" {
|
||||
# version = "v1"
|
||||
# metadata = {
|
||||
# name = "example-scoped-role"
|
||||
# }
|
||||
|
||||
# scope = "/staging"
|
||||
|
||||
# spec = {
|
||||
# assignable_scopes = ["/staging/aa"]
|
||||
# rules = [{
|
||||
# resources = ["scoped_token"]
|
||||
# verbs = ["read", "list"]
|
||||
# }]
|
||||
# }
|
||||
# }
|
||||
|
||||
resource "teleport_scoped_role_assignment" "example" {
|
||||
version = "v1"
|
||||
# sub_kind must be dynamic when creating scoped role assignments.
|
||||
sub_kind = "dynamic"
|
||||
metadata = {
|
||||
name = "test-scoped-role-assignment"
|
||||
}
|
||||
|
||||
scope = "/staging"
|
||||
|
||||
spec = {
|
||||
user = "will"
|
||||
assignments = [{
|
||||
role = "example-scoped-role"
|
||||
scope = "/staging/aa"
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{/* schema generated by tfplugindocs */}
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `metadata` (Attributes) Metadata contains the resource metadata. (see [below for nested schema](#nested-schema-for-metadata))
|
||||
- `scope` (String) Scope is the scope of the role assignment resource.
|
||||
- `spec` (Attributes) Spec is the role assignment specification. (see [below for nested schema](#nested-schema-for-spec))
|
||||
- `sub_kind` (String) SubKind is the resource sub-kind.
|
||||
- `version` (String) Version is the resource version.
|
||||
|
||||
### Nested Schema for `metadata`
|
||||
|
||||
Required:
|
||||
|
||||
- `name` (String) name is an object name.
|
||||
|
||||
Optional:
|
||||
|
||||
- `description` (String) description is object description.
|
||||
- `expires` (String) expires is a global expiry time header can be set on any resource in the system.
|
||||
- `labels` (Map of String) labels is a set of labels.
|
||||
|
||||
|
||||
### Nested Schema for `spec`
|
||||
|
||||
Required:
|
||||
|
||||
- `assignments` (Attributes List) Assignments is a list of individual role @ scope assignments. (see [below for nested schema](#nested-schema-for-specassignments))
|
||||
|
||||
Optional:
|
||||
|
||||
- `bot_name` (String) Name of the Bot to whom all contained assignments apply. Mutually exclusive with `user`.
|
||||
- `bot_scope` (String) Scope of the Bot to whom all contained assignments apply. Required if `bot_name` is set. If specified, assignment scopes must be equal or descendent of this scope.
|
||||
- `user` (String) User is the user to whom all contained assignments apply. Mutually exclusive with `bot_name`.
|
||||
|
||||
### Nested Schema for `spec.assignments`
|
||||
|
||||
Optional:
|
||||
|
||||
- `role` (String) Roles is the name of the role that is assigned by this assignment.
|
||||
- `scope` (String) Scope is the scope to which the role is assigned. This must be a member/child of the scope of the [ScopedRoleAssignment] in which this assignment is contained.
|
||||
@@ -192,6 +192,13 @@ endif
|
||||
--terraform_out=config=protoc-gen-terraform-scopedrole.yaml:./tfschema \
|
||||
teleport/scopes/access/v1/role.proto
|
||||
|
||||
@protoc \
|
||||
-I=../../api/proto \
|
||||
-I=$(PROTOBUF_MOD_PATH) \
|
||||
--plugin=$(PROTOC_GEN_TERRAFORM) \
|
||||
--terraform_out=config=protoc-gen-terraform-scopedroleassignment.yaml:./tfschema \
|
||||
teleport/scopes/access/v1/assignment.proto
|
||||
|
||||
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/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules_terraform.go ./tfschema/accessmonitoringrules/v1/
|
||||
@@ -206,6 +213,7 @@ endif
|
||||
mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/scopes/joining/v1/token_terraform.go ./tfschema/scopes/joining/v1/
|
||||
mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadcluster/v1/workloadcluster_terraform.go ./tfschema/workloadcluster/v1/
|
||||
mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/scopes/access/v1/role_terraform.go ./tfschema/scopes/access/v1/
|
||||
mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/scopes/access/v1/assignment_terraform.go ./tfschema/scopes/access/assignment/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
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# Teleport Scoped Role Assignment resource
|
||||
#
|
||||
# Assigns an existing scoped role to a user at a specific scope.
|
||||
# The referenced scoped role must already exist.
|
||||
|
||||
# resource "teleport_scoped_role" "example" {
|
||||
# version = "v1"
|
||||
# metadata = {
|
||||
# name = "example-scoped-role"
|
||||
# }
|
||||
|
||||
# scope = "/staging"
|
||||
|
||||
# spec = {
|
||||
# assignable_scopes = ["/staging/aa"]
|
||||
# rules = [{
|
||||
# resources = ["scoped_token"]
|
||||
# verbs = ["read", "list"]
|
||||
# }]
|
||||
# }
|
||||
# }
|
||||
|
||||
resource "teleport_scoped_role_assignment" "example" {
|
||||
version = "v1"
|
||||
# sub_kind must be dynamic when creating scoped role assignments.
|
||||
sub_kind = "dynamic"
|
||||
metadata = {
|
||||
name = "test-scoped-role-assignment"
|
||||
}
|
||||
|
||||
scope = "/staging"
|
||||
|
||||
spec = {
|
||||
user = "will"
|
||||
assignments = [{
|
||||
role = "example-scoped-role"
|
||||
scope = "/staging/aa"
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -1068,6 +1068,37 @@ var (
|
||||
DeleteRequest: "DeleteScopedRoleRequest",
|
||||
},
|
||||
}
|
||||
|
||||
scopedRoleAssignment = payload{
|
||||
Name: "ScopedRoleAssignment",
|
||||
TypeName: "ScopedRoleAssignment",
|
||||
VarName: "scopedRoleAssignment",
|
||||
GetMethod: "ScopedAccessServiceClient().GetScopedRoleAssignment",
|
||||
CreateMethod: "ScopedAccessServiceClient().CreateScopedRoleAssignment",
|
||||
UpdateMethod: "ScopedAccessServiceClient().UpsertScopedRoleAssignment",
|
||||
UpsertMethodArity: 2,
|
||||
DeleteMethod: "ScopedAccessServiceClient().DeleteScopedRoleAssignment",
|
||||
ID: "scopedRoleAssignment.Metadata.Name",
|
||||
Kind: "scoped_role_assignment",
|
||||
HasStaticID: false,
|
||||
ProtoPackage: "accessv1",
|
||||
ProtoPackagePath: "github.com/gravitational/teleport/api/gen/proto/go/teleport/scopes/access/v1",
|
||||
SchemaPackage: "assignmentschemav1",
|
||||
SchemaPackagePath: "github.com/gravitational/teleport/integrations/terraform/tfschema/scopes/access/assignment/v1",
|
||||
TerraformResourceType: "teleport_scoped_role_assignment",
|
||||
IsPlainStruct: true,
|
||||
ExtraImports: []string{"apitypes \"github.com/gravitational/teleport/lib/scopes/access\""},
|
||||
ForceSetKind: "apitypes.KindScopedRoleAssignment",
|
||||
RequestWrapper: &RequestWrapper{
|
||||
RequestResourceField: "Assignment",
|
||||
GetRequest: "GetScopedRoleAssignmentRequest",
|
||||
CreateRequest: "CreateScopedRoleAssignmentRequest",
|
||||
UpdateRequest: "UpsertScopedRoleAssignmentRequest",
|
||||
DeleteRequest: "DeleteScopedRoleAssignmentRequest",
|
||||
},
|
||||
DefaultSubKind: "\"dynamic\"",
|
||||
}
|
||||
|
||||
scopedToken = payload{
|
||||
Name: "ScopedToken",
|
||||
TypeName: "ScopedToken",
|
||||
@@ -1245,6 +1276,8 @@ func genTFSchema() {
|
||||
generateDataSource(retrievalModel, singularDataSource)
|
||||
generateResource(scopedRole, pluralResource)
|
||||
generateDataSource(scopedRole, pluralDataSource)
|
||||
generateResource(scopedRoleAssignment, pluralResource)
|
||||
generateDataSource(scopedRoleAssignment, pluralDataSource)
|
||||
generateResource(scopedToken, pluralResource)
|
||||
generateDataSource(scopedToken, pluralDataSource)
|
||||
generateResource(workloadCluster, pluralResource)
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
target_package_name: "v1"
|
||||
default_package_name: "github.com/gravitational/teleport/api/gen/proto/go/teleport/scopes/access/v1"
|
||||
use_state_for_unknown_by_default: true
|
||||
|
||||
# Top-level type names to export
|
||||
types:
|
||||
- "ScopedRoleAssignment"
|
||||
|
||||
# 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"
|
||||
"timestamppb": "google.golang.org/protobuf/types/known/timestamppb"
|
||||
"v1": "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1"
|
||||
"github_com_gravitational_teleport_integrations_terraform_tfschema": "github.com/gravitational/teleport/integrations/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:
|
||||
ScopedRoleAssignment:
|
||||
- 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:
|
||||
- "ScopedRoleAssignment.metadata.id"
|
||||
# Usage tracking is server-side only
|
||||
- "ScopedRoleAssignment.status"
|
||||
|
||||
# These fields will be marked as Computed: true
|
||||
computed_fields:
|
||||
- "ScopedRoleAssignment.metadata.expires"
|
||||
- "ScopedRoleAssignment.metadata.namespace"
|
||||
- "ScopedRoleAssignment.metadata.revision"
|
||||
- "ScopedRoleAssignment.kind"
|
||||
|
||||
# These fields will be marked as Required: true
|
||||
required_fields:
|
||||
- "ScopedRoleAssignment.metadata"
|
||||
- "ScopedRoleAssignment.metadata.name"
|
||||
- "ScopedRoleAssignment.version"
|
||||
- "ScopedRoleAssignment.sub_kind"
|
||||
- "ScopedRoleAssignment.scope"
|
||||
- "ScopedRoleAssignment.spec"
|
||||
- "ScopedRoleAssignment.spec.assignments"
|
||||
|
||||
sensitive_fields:
|
||||
|
||||
plan_modifiers:
|
||||
# Force to recreate resource if its 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"
|
||||
|
||||
validators:
|
||||
# Expires must be in the future
|
||||
Metadata.expires:
|
||||
- github_com_gravitational_teleport_integrations_terraform_tfschema.MustTimeBeInFuture()
|
||||
|
||||
custom_types:
|
||||
"ScopedRoleAssignment.metadata.expires": Timestamp
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2024 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"
|
||||
|
||||
accessv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/scopes/access/v1"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
assignmentschemav1 "github.com/gravitational/teleport/integrations/terraform/tfschema/scopes/access/assignment/v1"
|
||||
)
|
||||
|
||||
// dataSourceTeleportScopedRoleAssignmentType is the data source metadata type
|
||||
type dataSourceTeleportScopedRoleAssignmentType struct{}
|
||||
|
||||
// dataSourceTeleportScopedRoleAssignment is the resource
|
||||
type dataSourceTeleportScopedRoleAssignment struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportScopedRoleAssignmentType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return assignmentschemav1.GenSchemaScopedRoleAssignment(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportScopedRoleAssignmentType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportScopedRoleAssignment{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport ScopedRoleAssignment
|
||||
func (r dataSourceTeleportScopedRoleAssignment) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
var id types.String
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
var subKind types.String
|
||||
diags = req.Config.GetAttribute(ctx, path.Root("sub_kind"), &subKind)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
if subKind.Value == "" {
|
||||
subKind.Value = "dynamic"
|
||||
}
|
||||
|
||||
scopedRoleAssignmentGetResp, err := r.p.Client.ScopedAccessServiceClient().GetScopedRoleAssignment(ctx, &accessv1.GetScopedRoleAssignmentRequest{
|
||||
Name: id.Value,
|
||||
SubKind: subKind.Value,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ScopedRoleAssignment", trace.Wrap(err), "scoped_role_assignment"))
|
||||
return
|
||||
}
|
||||
scopedRoleAssignmentI := scopedRoleAssignmentGetResp.GetAssignment()
|
||||
|
||||
var state types.Object
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Todo: Remove after updating terraform-plugin to >=v1.5.0.
|
||||
// terraform-plugin-testing version <1.5.0 requires data resources to
|
||||
// implement the 'id' attribute.
|
||||
// https://developer.hashicorp.com/terraform/plugin/framework/acctests#no-id-found-in-attributes
|
||||
v, ok := state.Attrs["id"]
|
||||
if !ok || v.IsNull() {
|
||||
state.Attrs["id"] = id
|
||||
}
|
||||
|
||||
scopedRoleAssignment := scopedRoleAssignmentI
|
||||
|
||||
diags = assignmentschemav1.CopyScopedRoleAssignmentToTerraform(ctx, scopedRoleAssignment, &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
|
||||
}
|
||||
}
|
||||
@@ -586,6 +586,7 @@ func (p *Provider) GetResources(_ context.Context) (map[string]tfsdk.ResourceTyp
|
||||
"teleport_scoped_token": resourceTeleportScopedTokenType{},
|
||||
"teleport_workload_cluster": resourceTeleportWorkloadClusterType{},
|
||||
"teleport_scoped_role": resourceTeleportScopedRoleType{},
|
||||
"teleport_scoped_role_assignment": resourceTeleportScopedRoleAssignmentType{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -628,6 +629,7 @@ func (p *Provider) GetDataSources(_ context.Context) (map[string]tfsdk.DataSourc
|
||||
"teleport_integration": dataSourceTeleportIntegrationType{},
|
||||
"teleport_scoped_token": dataSourceTeleportScopedTokenType{},
|
||||
"teleport_scoped_role": dataSourceTeleportScopedRoleType{},
|
||||
"teleport_scoped_role_assignment": dataSourceTeleportScopedRoleAssignmentType{},
|
||||
// TODO(bl-nero): Add teleport_inference_* data sources after data sources
|
||||
// are fixed. The current problems with data sources include:
|
||||
// - Data sources only perform a "shallow fill", which means only setting
|
||||
|
||||
+411
@@ -0,0 +1,411 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2024 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"
|
||||
"strings"
|
||||
apitypes "github.com/gravitational/teleport/lib/scopes/access"
|
||||
|
||||
accessv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/scopes/access/v1"
|
||||
|
||||
"github.com/gravitational/teleport/api/utils/retryutils"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
assignmentschemav1 "github.com/gravitational/teleport/integrations/terraform/tfschema/scopes/access/assignment/v1"
|
||||
)
|
||||
|
||||
// resourceTeleportScopedRoleAssignmentType is the resource metadata type
|
||||
type resourceTeleportScopedRoleAssignmentType struct{}
|
||||
|
||||
// resourceTeleportScopedRoleAssignment is the resource
|
||||
type resourceTeleportScopedRoleAssignment struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportScopedRoleAssignmentType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return assignmentschemav1.GenSchemaScopedRoleAssignment(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportScopedRoleAssignmentType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportScopedRoleAssignment{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the ScopedRoleAssignment
|
||||
func (r resourceTeleportScopedRoleAssignment) 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
|
||||
}
|
||||
|
||||
scopedRoleAssignment := &accessv1.ScopedRoleAssignment{}
|
||||
diags = assignmentschemav1.CopyScopedRoleAssignmentFromTerraform(ctx, plan, scopedRoleAssignment)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
scopedRoleAssignmentResource := scopedRoleAssignment
|
||||
|
||||
scopedRoleAssignmentResource.Kind = apitypes.KindScopedRoleAssignment
|
||||
if scopedRoleAssignmentResource.SubKind == "" {
|
||||
scopedRoleAssignmentResource.SubKind = "dynamic"
|
||||
}
|
||||
|
||||
id := scopedRoleAssignmentResource.Metadata.Name
|
||||
|
||||
_, err = r.p.Client.ScopedAccessServiceClient().GetScopedRoleAssignment(ctx, &accessv1.GetScopedRoleAssignmentRequest{
|
||||
Name: id,
|
||||
SubKind: scopedRoleAssignmentResource.SubKind,
|
||||
})
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
existErr := fmt.Sprintf("ScopedRoleAssignment exists in Teleport. Either remove it (tctl rm scoped_role_assignment/%v)"+
|
||||
" or import it to the existing state (terraform import teleport_scoped_role_assignment.%v %v)", id, id, id)
|
||||
|
||||
resp.Diagnostics.Append(diagFromErr("ScopedRoleAssignment exists in Teleport", trace.Errorf(existErr)))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ScopedRoleAssignment", trace.Wrap(err), "scoped_role_assignment"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.ScopedAccessServiceClient().CreateScopedRoleAssignment(ctx, &accessv1.CreateScopedRoleAssignmentRequest{
|
||||
Assignment: scopedRoleAssignmentResource,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating ScopedRoleAssignment", trace.Wrap(err), "scoped_role_assignment"))
|
||||
return
|
||||
}
|
||||
var scopedRoleAssignmentI *accessv1.ScopedRoleAssignment
|
||||
// Try getting the resource until it exists.
|
||||
tries := 0
|
||||
retry, err := retryutils.NewRetryV2(retryutils.RetryV2Config{
|
||||
Driver: retryutils.NewExponentialDriver(r.p.RetryConfig.Base),
|
||||
First: r.p.RetryConfig.Base,
|
||||
Max: r.p.RetryConfig.Cap,
|
||||
Jitter: retryutils.HalfJitter,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for {
|
||||
tries = tries + 1
|
||||
scopedRoleAssignmentGetResp, getErr := r.p.Client.ScopedAccessServiceClient().GetScopedRoleAssignment(ctx, &accessv1.GetScopedRoleAssignmentRequest{
|
||||
Name: id,
|
||||
SubKind: scopedRoleAssignmentResource.SubKind,
|
||||
})
|
||||
err = getErr
|
||||
if err == nil {
|
||||
scopedRoleAssignmentI = scopedRoleAssignmentGetResp.GetAssignment()
|
||||
}
|
||||
if trace.IsNotFound(err) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ScopedRoleAssignment", trace.Wrap(ctx.Err()), "scoped_role_assignment"))
|
||||
return
|
||||
case <-retry.After():
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading ScopedRoleAssignment (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "scoped_role_assignment")
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ScopedRoleAssignment", trace.Wrap(err), "scoped_role_assignment"))
|
||||
return
|
||||
}
|
||||
|
||||
scopedRoleAssignmentResource = scopedRoleAssignmentI
|
||||
|
||||
scopedRoleAssignment = scopedRoleAssignmentResource
|
||||
|
||||
diags = assignmentschemav1.CopyScopedRoleAssignmentToTerraform(ctx, scopedRoleAssignment, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: scopedRoleAssignment.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport ScopedRoleAssignment
|
||||
func (r resourceTeleportScopedRoleAssignment) 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("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
var subKind types.String
|
||||
diags = req.State.GetAttribute(ctx, path.Root("sub_kind"), &subKind)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
if subKind.Value == "" {
|
||||
subKind.Value = "dynamic"
|
||||
}
|
||||
|
||||
scopedRoleAssignmentGetResp, err := r.p.Client.ScopedAccessServiceClient().GetScopedRoleAssignment(ctx, &accessv1.GetScopedRoleAssignmentRequest{
|
||||
Name: id.Value,
|
||||
SubKind: subKind.Value,
|
||||
})
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ScopedRoleAssignment", trace.Wrap(err), "scoped_role_assignment"))
|
||||
return
|
||||
}
|
||||
scopedRoleAssignmentI := scopedRoleAssignmentGetResp.GetAssignment()
|
||||
scopedRoleAssignment := scopedRoleAssignmentI
|
||||
diags = assignmentschemav1.CopyScopedRoleAssignmentToTerraform(ctx, scopedRoleAssignment, &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 ScopedRoleAssignment
|
||||
func (r resourceTeleportScopedRoleAssignment) 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
|
||||
}
|
||||
|
||||
scopedRoleAssignment := &accessv1.ScopedRoleAssignment{}
|
||||
diags = assignmentschemav1.CopyScopedRoleAssignmentFromTerraform(ctx, plan, scopedRoleAssignment)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
scopedRoleAssignmentResource := scopedRoleAssignment
|
||||
|
||||
scopedRoleAssignmentResource.Kind = apitypes.KindScopedRoleAssignment
|
||||
|
||||
|
||||
if scopedRoleAssignmentResource.SubKind == "" {
|
||||
scopedRoleAssignmentResource.SubKind = "dynamic"
|
||||
}
|
||||
name := scopedRoleAssignmentResource.Metadata.Name
|
||||
|
||||
scopedRoleAssignmentBeforeResp, err := r.p.Client.ScopedAccessServiceClient().GetScopedRoleAssignment(ctx, &accessv1.GetScopedRoleAssignmentRequest{
|
||||
Name: name,
|
||||
SubKind: scopedRoleAssignmentResource.SubKind,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ScopedRoleAssignment", err, "scoped_role_assignment"))
|
||||
return
|
||||
}
|
||||
scopedRoleAssignmentBefore := scopedRoleAssignmentBeforeResp.GetAssignment()
|
||||
|
||||
_, err = r.p.Client.ScopedAccessServiceClient().UpsertScopedRoleAssignment(ctx, &accessv1.UpsertScopedRoleAssignmentRequest{
|
||||
Assignment: scopedRoleAssignmentResource,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating ScopedRoleAssignment", err, "scoped_role_assignment"))
|
||||
return
|
||||
}
|
||||
var scopedRoleAssignmentI *accessv1.ScopedRoleAssignment
|
||||
|
||||
tries := 0
|
||||
retry, err := retryutils.NewRetryV2(retryutils.RetryV2Config{
|
||||
Driver: retryutils.NewExponentialDriver(r.p.RetryConfig.Base),
|
||||
First: r.p.RetryConfig.Base,
|
||||
Max: r.p.RetryConfig.Cap,
|
||||
Jitter: retryutils.HalfJitter,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for {
|
||||
tries = tries + 1
|
||||
scopedRoleAssignmentGetResp, getErr := r.p.Client.ScopedAccessServiceClient().GetScopedRoleAssignment(ctx, &accessv1.GetScopedRoleAssignmentRequest{
|
||||
Name: name,
|
||||
SubKind: scopedRoleAssignmentResource.SubKind,
|
||||
})
|
||||
err = getErr
|
||||
if err == nil {
|
||||
scopedRoleAssignmentI = scopedRoleAssignmentGetResp.GetAssignment()
|
||||
}
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ScopedRoleAssignment", err, "scoped_role_assignment"))
|
||||
return
|
||||
}
|
||||
if scopedRoleAssignmentBefore.GetMetadata().Revision != scopedRoleAssignmentI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ScopedRoleAssignment", trace.Wrap(ctx.Err()), "scoped_role_assignment"))
|
||||
return
|
||||
case <-retry.After():
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading ScopedRoleAssignment (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "scoped_role_assignment")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
scopedRoleAssignmentResource = scopedRoleAssignmentI
|
||||
|
||||
diags = assignmentschemav1.CopyScopedRoleAssignmentToTerraform(ctx, scopedRoleAssignment, &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 ScopedRoleAssignment
|
||||
func (r resourceTeleportScopedRoleAssignment) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var id types.String
|
||||
diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
var subKind types.String
|
||||
diags = req.State.GetAttribute(ctx, path.Root("sub_kind"), &subKind)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
if subKind.Value == "" {
|
||||
subKind.Value = "dynamic"
|
||||
}
|
||||
|
||||
_, err := r.p.Client.ScopedAccessServiceClient().DeleteScopedRoleAssignment(ctx, &accessv1.DeleteScopedRoleAssignmentRequest{
|
||||
Name: id.Value,
|
||||
SubKind: subKind.Value,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting ScopedRoleAssignment", trace.Wrap(err), "scoped_role_assignment"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports ScopedRoleAssignment state
|
||||
func (r resourceTeleportScopedRoleAssignment) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
subKind := "dynamic"
|
||||
name := req.ID
|
||||
if before, after, ok := strings.Cut(req.ID, "/"); ok {
|
||||
subKind = before
|
||||
name = after
|
||||
}
|
||||
scopedRoleAssignmentGetResp, err := r.p.Client.ScopedAccessServiceClient().GetScopedRoleAssignment(ctx, &accessv1.GetScopedRoleAssignmentRequest{
|
||||
Name: name,
|
||||
SubKind: subKind,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ScopedRoleAssignment", trace.Wrap(err), "scoped_role_assignment"))
|
||||
return
|
||||
}
|
||||
scopedRoleAssignment := scopedRoleAssignmentGetResp.GetAssignment()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ScopedRoleAssignment", trace.Wrap(err), "scoped_role_assignment"))
|
||||
return
|
||||
}
|
||||
|
||||
scopedRoleAssignmentResource := scopedRoleAssignment
|
||||
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = assignmentschemav1.CopyScopedRoleAssignmentToTerraform(ctx, scopedRoleAssignmentResource, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := scopedRoleAssignment.Metadata.Name
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
resource "teleport_scoped_role_assignment" "test" {
|
||||
version = "v1"
|
||||
metadata = {
|
||||
name = "test-scoped-role-assignment"
|
||||
}
|
||||
scope = "/staging"
|
||||
sub_kind = "dynamic"
|
||||
spec = {
|
||||
user = "testuser"
|
||||
assignments = [{
|
||||
role = "test-scoped-role"
|
||||
scope = "/staging/aa"
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
resource "teleport_scoped_role_assignment" "test" {
|
||||
version = "v1"
|
||||
metadata = {
|
||||
name = "test-scoped-role-assignment"
|
||||
}
|
||||
scope = "/staging"
|
||||
sub_kind = "dynamic"
|
||||
spec = {
|
||||
user = "testuser"
|
||||
assignments = [{
|
||||
role = "test-scoped-role"
|
||||
scope = "/staging/aaaa"
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
// Teleport
|
||||
// Copyright (C) 2026 Gravitational, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package testlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1"
|
||||
accessv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/scopes/access/v1"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/lib/scopes/access"
|
||||
)
|
||||
|
||||
func (s *TerraformSuiteOSS) TestScopedRoleAssignment() {
|
||||
t := s.T()
|
||||
ctx := t.Context()
|
||||
t.Setenv("TELEPORT_UNSTABLE_SCOPES", "yes")
|
||||
|
||||
checkDestroyed := func(state *terraform.State) error {
|
||||
accessClient := s.client.ScopedAccessServiceClient()
|
||||
|
||||
_, err := accessClient.GetScopedRoleAssignment(ctx, &accessv1.GetScopedRoleAssignmentRequest{
|
||||
Name: "test-scoped-role-assignment",
|
||||
SubKind: access.SubKindDynamic,
|
||||
})
|
||||
if !trace.IsNotFound(err) {
|
||||
return trace.Errorf("expected not found, actual: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
name := "teleport_scoped_role_assignment.test"
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
CheckDestroy: checkDestroyed,
|
||||
IsUnitTest: true,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.getFixture("scoped_role_assignment_0_create.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", access.KindScopedRoleAssignment),
|
||||
resource.TestCheckResourceAttr(name, "sub_kind", access.SubKindDynamic),
|
||||
resource.TestCheckResourceAttr(name, "scope", "/staging"),
|
||||
resource.TestCheckResourceAttr(name, "spec.user", "testuser"),
|
||||
resource.TestCheckResourceAttr(name, "spec.assignments.0.role", "test-scoped-role"),
|
||||
resource.TestCheckResourceAttr(name, "spec.assignments.0.scope", "/staging/aa"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("scoped_role_assignment_1_update.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "scope", "/staging"),
|
||||
resource.TestCheckResourceAttr(name, "spec.assignments.0.role", "test-scoped-role"),
|
||||
resource.TestCheckResourceAttr(name, "spec.assignments.0.scope", "/staging/aaaa"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("scoped_role_assignment_1_update.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TerraformSuiteOSS) TestImportScopedRoleAssignment() {
|
||||
t := s.T()
|
||||
ctx := t.Context()
|
||||
t.Setenv("TELEPORT_UNSTABLE_SCOPES", "yes")
|
||||
accessClient := s.client.ScopedAccessServiceClient()
|
||||
|
||||
r := "teleport_scoped_role_assignment"
|
||||
id := "test-import-sra"
|
||||
name := r + "." + id
|
||||
|
||||
assignment := &accessv1.ScopedRoleAssignment{
|
||||
Kind: access.KindScopedRoleAssignment,
|
||||
SubKind: access.SubKindDynamic,
|
||||
Version: types.V1,
|
||||
Metadata: &headerv1.Metadata{
|
||||
Name: id,
|
||||
},
|
||||
Scope: "/staging",
|
||||
Spec: &accessv1.ScopedRoleAssignmentSpec{
|
||||
User: "testuser",
|
||||
Assignments: []*accessv1.Assignment{
|
||||
{
|
||||
Role: "test-scoped-role",
|
||||
Scope: "/staging/aa",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := accessClient.CreateScopedRoleAssignment(ctx, &accessv1.CreateScopedRoleAssignmentRequest{
|
||||
Assignment: assignment,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.EventuallyWithT(t, func(t *assert.CollectT) {
|
||||
_, err := accessClient.GetScopedRoleAssignment(ctx, &accessv1.GetScopedRoleAssignmentRequest{
|
||||
Name: id,
|
||||
SubKind: access.SubKindDynamic,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}, 5*time.Second, time.Second)
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
IsUnitTest: true,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: fmt.Sprintf("%s\nresource %q %q { }", s.terraformConfig, r, id),
|
||||
ResourceName: name,
|
||||
ImportState: true,
|
||||
ImportStateId: id,
|
||||
ImportStateCheck: func(state []*terraform.InstanceState) error {
|
||||
require.Equal(t, access.KindScopedRoleAssignment, state[0].Attributes["kind"])
|
||||
require.Equal(t, "/staging", state[0].Attributes["scope"])
|
||||
require.Equal(t, "testuser", state[0].Attributes["spec.user"])
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
Copyright 2026 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 "github.com/gravitational/teleport/integrations/terraform/tfschema/resource153"
|
||||
|
||||
var (
|
||||
GenSchemaTimestamp = resource153.GenSchemaTimestamp
|
||||
CopyToTimestamp = resource153.CopyToTimestamp
|
||||
CopyFromTimestamp = resource153.CopyFromTimestamp
|
||||
)
|
||||
@@ -845,6 +845,7 @@ func NewPresetTerraformProviderRole() types.Role {
|
||||
types.NewRule(types.KindSAMLIdPServiceProvider, RW()),
|
||||
types.NewRule(types.KindScopedToken, RW()),
|
||||
types.NewRule(access.KindScopedRole, RW()),
|
||||
types.NewRule(access.KindScopedRoleAssignment, RW()),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -780,6 +780,7 @@ func TestAddRoleDefaults(t *testing.T) {
|
||||
types.NewRule(types.KindRetrievalModel, RW()),
|
||||
types.NewRule(types.KindScopedToken, RW()),
|
||||
types.NewRule(access.KindScopedRole, RW()),
|
||||
types.NewRule(access.KindScopedRoleAssignment, RW()),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user