Repository: honeycombio/terraform-provider-honeycombio (go)

Issue:
data source: auth metadata
Build a new data source to query the data from the `/1/auth` endpoint and make it available to Terraform configurations (potential name `honeycombio_auth_metadata`?)

Would expose at minimum the team and environment names and slugs. Unclear if the key permissions are of interest?

Pull request description:
Add auth metadata data source
Adds new data source `honeycombio_auth_metadata` to enable pulling Authorization, Environment, or Team into Terraform configurations.

- Closes #247 




Patch:
diff --git a/client/auth.go b/client/auth.go
new file mode 100644
index 0000000..e39455e
--- /dev/null
+++ b/client/auth.go
@@ -0,0 +1,53 @@
+package client
+
+import (
+	"context"
+)
+
+// The Auth endpoint lists authorizations that have been granted for an API key
+// within a team and environment.
+//
+// API docs: https://docs.honeycomb.io/api/auth/
+type Auth interface {
+	// List all authorizations for this API key in this team and environment.
+	List(ctx context.Context) (AuthMetadata, error)
+}
+
+// auth implements Auth.
+type auth struct {
+	client *Client
+}
+
+// Compile-time proof of interface implementation by type auth.
+var _ Auth = (*auth)(nil)
+
+type AuthMetadata struct {
+	// Authorizations granted to this API key.
+	APIKeyAccess struct {
+		Boards         bool `json:"boards"`
+		Columns        bool `json:"columns"`
+		CreateDatasets bool `json:"create_datasets"`
+		Events         bool `json:"events"`
+		Markers        bool `json:"markers"`
+		Queries        bool `json:"queries"`
+		Recipients     bool `json:"recipients"`
+		SLOs           bool `json:"slos"`
+		Triggers       bool `json:"triggers"`
+	} `json:"api_key_access"`
+	Environment struct {
+		// Name is empty for Classic environments.
+		Name string `json:"name"`
+		// Slug is empty for Classic environments.
+		Slug string `json:"slug"`
+	} `json:"environment"`
+	Team struct {
+		Name string `json:"name"`
+		Slug string `json:"slug"`
+	} `json:"team"`
+}
+
+func (s *auth) List(ctx context.Context) (AuthMetadata, error) {
+	var r AuthMetadata
+	err := s.client.performRequest(ctx, "GET", "/1/auth", nil, &r)
+	return r, err
+}
diff --git a/client/client.go b/client/client.go
index 514f904..f6acbb5 100644
--- a/client/client.go
+++ b/client/client.go
@@ -61,6 +61,7 @@ type Client struct {
 	userAgent  string
 	httpClient *http.Client
 
+	Auth               Auth
 	Boards             Boards
 	Columns            Columns
 	Datasets           Datasets
@@ -101,6 +102,7 @@ func NewClient(config *Config) (*Client, error) {
 		userAgent:  cfg.UserAgent,
 		httpClient: httpClient,
 	}
+	client.Auth = &auth{client: client}
 	client.Boards = &boards{client: client}
 	client.Columns = &columns{client: client}
 	client.Datasets = &datasets{client: client}
@@ -119,6 +121,17 @@ func NewClient(config *Config) (*Client, error) {
 	return client, nil
 }
 
+// IsClassic returns true if the client is configured with a Classic API Key
+//
+// If there is an error fetching the auth metadata, this will return false.
+func (c *Client) IsClassic(ctx context.Context) bool {
+	metadata, err := c.Auth.List(ctx)
+	if err != nil {
+		return false
+	}
+	return metadata.Environment.Slug == ""
+}
+
 // ErrNotFound is returned when the requested item could not be found.
 var ErrNotFound = errors.New("404 Not Found")
 
diff --git a/client/trigger.go b/client/trigger.go
index 2e97150..98dd5da 100644
--- a/client/trigger.go
+++ b/client/trigger.go
@@ -79,8 +79,9 @@ type Trigger struct {
 
 // TriggerThreshold represents the threshold of a trigger.
 type TriggerThreshold struct {
-	Op    TriggerThresholdOp `json:"op"`
-	Value float64            `json:"value"`
+	Op            TriggerThresholdOp `json:"op"`
+	Value         float64            `json:"value"`
+	ExceededLimit int                `json:"exceeded_limit,omitempty"`
 }
 
 // TriggerThresholdOp the operator of the trigger threshold.
diff --git a/docs/data-sources/auth_metadata.md b/docs/data-sources/auth_metadata.md
new file mode 100644
index 0000000..360bbff
--- /dev/null
+++ b/docs/data-sources/auth_metadata.md
@@ -0,0 +1,48 @@
+# Data Source: honeycombio_auth_metadata
+
+The `honeycombio_auth_metadata` data source retreives information about the API key used to authenticate the provider.
+
+## Example Usage
+
+```hcl
+data "honeycombio_auth_metadata" "current" {}
+
+output "team_name" {
+  value = honeycombio_auth_metadata.current.team.name
+}
+
+output "environment_slug" {
+  value = honeycombio_auth_metadata.current.environment.slug
+} 
+
+output "slo_management_access" {
+  value = honeycombio_auth_metadata.current.api_key_access.slos
+}
+```
+
+## Argument Reference
+
+There are no arguments available for this data source.
+
+## Attribute Reference
+
+This data source exports the following attributes in addition to the arguments above:
+
+* `api_key_access` - The authorizations granted for the API key used to authenticate the provider.
+  See [the documentation](https://docs.honeycomb.io/working-with-your-data/settings/api-keys/) for more information.
+  * `boards` - `true` if this API key can create and manage Boards.
+  * `columns` - `true` if this API key can create and manage can create and manage Queries, Columns, Derived Columns, and Query Annotations
+  * `datasets` - `true` if this API key can create and manage Datasets.
+  * `events` - `true` if this API key can key can send events to Honeycomb.
+  * `markers` - `true` if this API key can create and manage Markers.
+  * `queries` - `true` if this API key can execute existing Queries via the Query Data API.
+  * `recipients` - `true` if this API key can create and manage Recipients.
+  * `slos` - `true` if this API key can create and manage SLOs.
+  * `triggers` - `true` if this API key can create and manage Triggers.
+* `environment` - Information about the Environment the API key is scoped to.
+  * `classic` - `true` if this API key belongs to a [Honeycomb Classic](https://docs.honeycomb.io/honeycomb-classic/) environment.
+  * `name` - The name of the Environment. For Classic environments, this will be null.
+  * `slug` - The slug of the Environment. For Classic environments, this will be null.
+* `team` - Information about the Team the API key is scoped to.
+  * `name` - The name of the Team.
+  * `slug` - The slug of the Team.
diff --git a/docs/resources/trigger.md b/docs/resources/trigger.md
index a95890e..9fdba82 100644
--- a/docs/resources/trigger.md
+++ b/docs/resources/trigger.md
@@ -101,15 +101,16 @@ resource "honeycombio_trigger" "example" {
   frequency = 600 // in seconds, 10 minutes
 
   threshold {
-    op    = ">"
-    value = 1000
+    op             = ">"
+    value          = 1000
+    exceeded_limit = 3
   }
 
   recipient {
     id = data.honeycombio_recipient.pd-prod.id
 
     notification_details {
-      pagerduty_severity = "info"
+      pagerduty_severity = "warning"
     }
   }
 
@@ -153,6 +154,7 @@ Each trigger configuration must contain exactly one `threshold` block, which acc
 
 * `op` - (Required) The operator to apply, allowed threshold operators are `>`, `>=`, `<`, and `<=`.
 * `value` - (Required) The value to be used with the operator.
+* `exceeded_limit` - (Optional) The number of times the threshold is met before an alert is sent, must be between 1 and 5. Defaults to `1`.
 
 Each trigger configuration may provide an `evaluation_schedule` block, which accepts the following arguments:
 
diff --git a/internal/helper/modifiers/trigger_threshold.go b/internal/helper/modifiers/trigger_threshold.go
new file mode 100644
index 0000000..882ca27
--- /dev/null
+++ b/internal/helper/modifiers/trigger_threshold.go
@@ -0,0 +1,36 @@
+package modifiers
+
+import (
+	"context"
+
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+type defaultTriggerThresholdExceededLimitModifier struct{}
+
+var _ planmodifier.Int64 = &defaultTriggerThresholdExceededLimitModifier{}
+
+func (m defaultTriggerThresholdExceededLimitModifier) Description(_ context.Context) string {
+	return "Handles the default value for a Trigger's Threshold Exceeded Limits."
+}
+
+func (m defaultTriggerThresholdExceededLimitModifier) MarkdownDescription(ctx context.Context) string {
+	return m.Description(ctx)
+}
+
+func (m defaultTriggerThresholdExceededLimitModifier) PlanModifyInt64(ctx context.Context, req planmodifier.Int64Request, resp *planmodifier.Int64Response) {
+	// Do nothing on resource destroy.
+	if req.Plan.Raw.IsNull() {
+		return
+	}
+
+	if req.ConfigValue.IsNull() {
+		// default value is 1
+		resp.PlanValue = types.Int64Value(1)
+	}
+}
+
+func DefaultTriggerThresholdExceededLimit() planmodifier.Int64 {
+	return defaultTriggerThresholdExceededLimitModifier{}
+}
diff --git a/internal/models/triggers.go b/internal/models/triggers.go
index 9e8bc81..7c052be 100644
--- a/internal/models/triggers.go
+++ b/internal/models/triggers.go
@@ -17,8 +17,9 @@ type TriggerResourceModel struct {
 }
 
 type TriggerThresholdModel struct {
-	Op    types.String  `tfsdk:"op"`
-	Value types.Float64 `tfsdk:"value"`
+	Op            types.String  `tfsdk:"op"`
+	Value         types.Float64 `tfsdk:"value"`
+	ExceededLimit types.Int64   `tfsdk:"exceeded_limit"`
 }
 
 type TriggerEvaluationScheduleModel struct {
diff --git a/internal/provider/auth_metadata_data_source.go b/internal/provider/auth_metadata_data_source.go
new file mode 100644
index 0000000..10261b5
--- /dev/null
+++ b/internal/provider/auth_metadata_data_source.go
@@ -0,0 +1,203 @@
+package provider
+
+import (
+	"context"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource"
+	"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+
+	"github.com/honeycombio/terraform-provider-honeycombio/client"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ datasource.DataSource              = &authMetadataDataSource{}
+	_ datasource.DataSourceWithConfigure = &authMetadataDataSource{}
+)
+
+func NewAuthMetadataDataSource() datasource.DataSource {
+	return &authMetadataDataSource{}
+}
+
+// authMetadataDataSource is the data source implementation.
+type authMetadataDataSource struct {
+	client *client.Client
+}
+
+type authMetadataDataSourceModel struct {
+	APIKeyAccess struct {
+		Boards     types.Bool `tfsdk:"boards"`
+		Columns    types.Bool `tfsdk:"columns"`
+		Datasets   types.Bool `tfsdk:"datasets"`
+		Events     types.Bool `tfsdk:"events"`
+		Markers    types.Bool `tfsdk:"markers"`
+		Queries    types.Bool `tfsdk:"queries"`
+		Recipients types.Bool `tfsdk:"recipients"`
+		SLOs       types.Bool `tfsdk:"slos"`
+		Triggers   types.Bool `tfsdk:"triggers"`
+	} `tfsdk:"api_key_access"`
+	Environment struct {
+		Classic types.Bool   `tfsdk:"classic"`
+		Name    types.String `tfsdk:"name"`
+		Slug    types.String `tfsdk:"slug"`
+	} `tfsdk:"environment"`
+	Team struct {
+		Name types.String `tfsdk:"name"`
+		Slug types.String `tfsdk:"slug"`
+	} `tfsdk:"team"`
+}
+
+func (d *authMetadataDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+	resp.TypeName = req.ProviderTypeName + "_auth_metadata"
+}
+
+func (d *authMetadataDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+	resp.Schema = schema.Schema{
+		Description: "Retreives information about the API key used to authenticate the provider.",
+		Blocks: map[string]schema.Block{
+			"api_key_access": schema.SingleNestedBlock{
+				Description: "The authorizations granted for the API key used to authenticate the provider. See https://docs.honeycomb.io/working-with-your-data/settings/api-keys/ for more information.",
+				Attributes: map[string]schema.Attribute{
+					"boards": schema.BoolAttribute{
+						Description: "Whether this API key can create and manage Boards.",
+						Computed:    true,
+						Optional:    false,
+						Required:    false,
+					},
+					"columns": schema.BoolAttribute{
+						Description: "Whether this API key can create and manage Queries, Columns, Derived Columns, and Query Annotations.",
+						Computed:    true,
+						Optional:    false,
+						Required:    false,
+					},
+					"datasets": schema.BoolAttribute{
+						Description: "Whether this API key can create and manage Datasets.",
+						Computed:    true,
+						Optional:    false,
+						Required:    false,
+					},
+					"events": schema.BoolAttribute{
+						Description: "Whether this API key can send events to Honeycomb.",
+						Computed:    true,
+						Optional:    false,
+						Required:    false,
+					},
+					"markers": schema.BoolAttribute{
+						Description: "Whether this API key can create and manage Markers.",
+						Computed:    true,
+						Optional:    false,
+						Required:    false,
+					},
+					"queries": schema.BoolAttribute{
+						Description: "Whether this API key can execute existing Queries via the Query Data API.",
+						Computed:    true,
+						Optional:    false,
+						Required:    false,
+					},
+					"recipients": schema.BoolAttribute{
+						Description: "Whether this API key can create and manage Recipients.",
+						Computed:    true,
+						Optional:    false,
+						Required:    false,
+					},
+					"slos": schema.BoolAttribute{
+						Description: "Whether this API key can create and manage SLOs and Burn Alerts.",
+						Computed:    true,
+						Optional:    false,
+						Required:    false,
+					},
+					"triggers": schema.BoolAttribute{
+						Description: "Whether this API key can create and manage Triggers.",
+						Computed:    true,
+						Optional:    false,
+						Required:    false,
+					},
+				},
+			},
+			"environment": schema.SingleNestedBlock{
+				Description: "Information about the Environment the API key is scoped to.",
+				Attributes: map[string]schema.Attribute{
+					"classic": schema.BoolAttribute{
+						Description: "Whether the Environment is a Classic Environment.",
+						Computed:    true,
+						Optional:    false,
+						Required:    false,
+					},
+					"name": schema.StringAttribute{
+						Description: "The name of the Environment. For Classic environments, this will be null.",
+						Computed:    true,
+						Optional:    false,
+						Required:    false,
+					},
+					"slug": schema.StringAttribute{
+						Description: "The slug of the Environment. For Classic environments, this will be null.",
+						Computed:    true,
+						Optional:    false,
+						Required:    false,
+					},
+				},
+			},
+			"team": schema.SingleNestedBlock{
+				Description: "Information about the Team the API key is scoped to.",
+				Attributes: map[string]schema.Attribute{
+					"name": schema.StringAttribute{
+						Description: "The name of the Team.",
+						Computed:    true,
+						Optional:    false,
+						Required:    false,
+					},
+					"slug": schema.StringAttribute{
+						Description: "The slug of the Team.",
+						Computed:    true,
+						Optional:    false,
+						Required:    false,
+					},
+				},
+			},
+		},
+	}
+}
+
+func (d *authMetadataDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, _ *datasource.ConfigureResponse) {
+	d.client = getClientFromDatasourceRequest(&req)
+}
+
+func (d *authMetadataDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+	var data authMetadataDataSourceModel
+
+	metadata, err := d.client.Auth.List(ctx)
+	if err != nil {
+		resp.Diagnostics.AddError(
+			"Unable to list Auth Metadata",
+			err.Error())
+		return
+	}
+
+	data.APIKeyAccess.Boards = types.BoolValue(metadata.APIKeyAccess.Boards)
+	data.APIKeyAccess.Columns = types.BoolValue(metadata.APIKeyAccess.Columns)
+	data.APIKeyAccess.Datasets = types.BoolValue(metadata.APIKeyAccess.CreateDatasets)
+	data.APIKeyAccess.Events = types.BoolValue(metadata.APIKeyAccess.Events)
+	data.APIKeyAccess.Markers = types.BoolValue(metadata.APIKeyAccess.Markers)
+	data.APIKeyAccess.Queries = types.BoolValue(metadata.APIKeyAccess.Queries)
+	data.APIKeyAccess.Recipients = types.BoolValue(metadata.APIKeyAccess.Recipients)
+	data.APIKeyAccess.SLOs = types.BoolValue(metadata.APIKeyAccess.SLOs)
+	data.APIKeyAccess.Triggers = types.BoolValue(metadata.APIKeyAccess.Triggers)
+
+	data.Team.Name = types.StringValue(metadata.Team.Name)
+	data.Team.Slug = types.StringValue(metadata.Team.Slug)
+
+	// Classic environments don't have a name or slug
+	if metadata.Environment.Slug == "" {
+		data.Environment.Classic = types.BoolValue(true)
+		data.Environment.Name = types.StringNull()
+		data.Environment.Slug = types.StringNull()
+	} else {
+		data.Environment.Classic = types.BoolValue(false)
+		data.Environment.Name = types.StringValue(metadata.Environment.Name)
+		data.Environment.Slug = types.StringValue(metadata.Environment.Slug)
+	}
+
+	diags := resp.State.Set(ctx, &data)
+	resp.Diagnostics.Append(diags...)
+}
diff --git a/internal/provider/provider.go b/internal/provider/provider.go
index a1b8f0f..06184e3 100644
--- a/internal/provider/provider.go
+++ b/internal/provider/provider.go
@@ -68,6 +68,7 @@ func (p *HoneycombioProvider) Resources(ctx context.Context) []func() resource.R
 
 func (p *HoneycombioProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
 	return []func() datasource.DataSource{
+		NewAuthMetadataDataSource,
 		NewDerivedColumnDataSource,
 		NewDerivedColumnsDataSource,
 		NewSLODataSource,
diff --git a/internal/provider/trigger_resource.go b/internal/provider/trigger_resource.go
index b496082..e1f712a 100644
--- a/internal/provider/trigger_resource.go
+++ b/internal/provider/trigger_resource.go
@@ -15,6 +15,7 @@ import (
 	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
 	"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
 	"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
 	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
 	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
 	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
@@ -24,6 +25,7 @@ import (
 
 	"github.com/honeycombio/terraform-provider-honeycombio/client"
 	"github.com/honeycombio/terraform-provider-honeycombio/internal/helper"
+	"github.com/honeycombio/terraform-provider-honeycombio/internal/helper/modifiers"
 	"github.com/honeycombio/terraform-provider-honeycombio/internal/helper/validation"
 	"github.com/honeycombio/terraform-provider-honeycombio/internal/models"
 )
@@ -144,6 +146,17 @@ func (r *triggerResource) Schema(_ context.Context, _ resource.SchemaRequest, re
 							Required:    true,
 							Description: "The value to be used with the operator.",
 						},
+						"exceeded_limit": schema.Int64Attribute{
+							Optional:    true,
+							Computed:    true,
+							Description: "The number of times the threshold is met before an alert is sent. Defaults to 1.",
+							Validators:  []validator.Int64{int64validator.Between(1, 5)},
+							PlanModifiers: []planmodifier.Int64{
+								int64planmodifier.UseStateForUnknown(),
+								// Nested blocks currently do not support default values, so we need to set it via a planmodifier
+								modifiers.DefaultTriggerThresholdExceededLimit(),
+							},
+						},
 					},
 				},
 			},
@@ -402,15 +415,17 @@ func expandTriggerThreshold(t []models.TriggerThresholdModel) *client.TriggerThr
 	}
 
 	return &client.TriggerThreshold{
-		Op:    client.TriggerThresholdOp(t[0].Op.ValueString()),
-		Value: t[0].Value.ValueFloat64(),
+		Op:            client.TriggerThresholdOp(t[0].Op.ValueString()),
+		Value:         t[0].Value.ValueFloat64(),
+		ExceededLimit: int(t[0].ExceededLimit.ValueInt64()),
 	}
 }
 
 func flattenTriggerThreshold(t *client.TriggerThreshold) []models.TriggerThresholdModel {
 	return []models.TriggerThresholdModel{{
-		Op:    types.StringValue(string(t.Op)),
-		Value: types.Float64Value(t.Value),
+		Op:            types.StringValue(string(t.Op)),
+		Value:         types.Float64Value(t.Value),
+		ExceededLimit: types.Int64Value(int64(t.ExceededLimit)),
 	}}
 }
 


Test patch:
diff --git a/client/auth_test.go b/client/auth_test.go
new file mode 100644
index 0000000..3594608
--- /dev/null
+++ b/client/auth_test.go
@@ -0,0 +1,30 @@
+package client
+
+import (
+	"context"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestAuthMetadata(t *testing.T) {
+	ctx := context.Background()
+	c := newTestClient(t)
+
+	t.Run("List", func(t *testing.T) {
+		metadata, err := c.Auth.List(ctx)
+
+		assert.NoError(t, err)
+		assert.NotEmpty(t, metadata.APIKeyAccess)
+		assert.NotEmpty(t, metadata.Team.Name)
+		assert.NotEmpty(t, metadata.Team.Slug)
+		// classic environment's don't have environment name and slugs
+		if len(c.apiKey) == 32 {
+			assert.Empty(t, metadata.Environment.Name)
+			assert.Empty(t, metadata.Environment.Slug)
+		} else {
+			assert.NotEmpty(t, metadata.Environment.Name)
+			assert.NotEmpty(t, metadata.Environment.Slug)
+		}
+	})
+}
diff --git a/client/client_test.go b/client/client_test.go
index f4da1e1..7431e40 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -89,3 +89,10 @@ func TestClient_parseHoneycombioError(t *testing.T) {
 
 	assert.Error(t, err, "400 Bad Request: request body should not be empty")
 }
+
+func TestClient_IsClassic(t *testing.T) {
+	ctx := context.Background()
+	c := newTestClient(t)
+
+	assert.Equal(t, len(c.apiKey) == 32, c.IsClassic(ctx))
+}
diff --git a/client/trigger_test.go b/client/trigger_test.go
index 04662b5..169558d 100644
--- a/client/trigger_test.go
+++ b/client/trigger_test.go
@@ -76,6 +76,8 @@ func TestTriggers(t *testing.T) {
 		data.AlertType = TriggerAlertTypeOnChange
 		// set the default evaluation window type
 		data.EvaluationScheduleType = TriggerEvaluationScheduleFrequency
+		// set the default threshold exceeded limit
+		data.Threshold.ExceededLimit = 1
 
 		assert.Equal(t, data, trigger)
 	})
@@ -108,6 +110,8 @@ func TestTriggers(t *testing.T) {
 				EndTime:    "21:00",
 			},
 		}
+		// update the threshold exceeded limit to 3
+		trigger.Threshold.ExceededLimit = 3
 
 		result, err := c.Triggers.Update(ctx, dataset, trigger)
 
diff --git a/internal/provider/auth_metadata_data_source_test.go b/internal/provider/auth_metadata_data_source_test.go
new file mode 100644
index 0000000..95092e2
--- /dev/null
+++ b/internal/provider/auth_metadata_data_source_test.go
@@ -0,0 +1,43 @@
+package provider
+
+import (
+	"context"
+	"fmt"
+	"testing"
+
+	"github.com/hashicorp/terraform-plugin-testing/helper/resource"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestAcc_AuthMetadataData(t *testing.T) {
+	ctx := context.Background()
+	c := testAccClient(t)
+
+	metadata, err := c.Auth.List(ctx)
+	require.NoError(t, err)
+
+	resource.Test(t, resource.TestCase{
+		PreCheck:                 testAccPreCheck(t),
+		ProtoV5ProviderFactories: testAccProtoV5ProviderFactory,
+		Steps: []resource.TestStep{
+			{
+				Config: `data "honeycombio_auth_metadata" "test" {}`,
+				Check: resource.ComposeAggregateTestCheckFunc(
+					resource.TestCheckResourceAttr("data.honeycombio_auth_metadata.test", "team.name", metadata.Team.Name),
+					resource.TestCheckResourceAttr("data.honeycombio_auth_metadata.test", "team.slug", metadata.Team.Slug),
+					resource.TestCheckResourceAttr("data.honeycombio_auth_metadata.test", "environment.classic", fmt.Sprintf("%v", c.IsClassic(ctx))),
+					resource.TestCheckResourceAttr("data.honeycombio_auth_metadata.test", "api_key_access.boards", fmt.Sprintf("%v", metadata.APIKeyAccess.Boards)),
+					resource.TestCheckResourceAttr("data.honeycombio_auth_metadata.test", "api_key_access.columns", fmt.Sprintf("%v", metadata.APIKeyAccess.Columns)),
+					resource.TestCheckResourceAttr("data.honeycombio_auth_metadata.test", "api_key_access.datasets", fmt.Sprintf("%v", metadata.APIKeyAccess.CreateDatasets)),
+					resource.TestCheckResourceAttr("data.honeycombio_auth_metadata.test", "api_key_access.events", fmt.Sprintf("%v", metadata.APIKeyAccess.Events)),
+					resource.TestCheckResourceAttr("data.honeycombio_auth_metadata.test", "api_key_access.markers", fmt.Sprintf("%v", metadata.APIKeyAccess.Markers)),
+					resource.TestCheckResourceAttr("data.honeycombio_auth_metadata.test", "api_key_access.queries", fmt.Sprintf("%v", metadata.APIKeyAccess.Queries)),
+					resource.TestCheckResourceAttr("data.honeycombio_auth_metadata.test", "api_key_access.recipients", fmt.Sprintf("%v", metadata.APIKeyAccess.Recipients)),
+					resource.TestCheckResourceAttr("data.honeycombio_auth_metadata.test", "api_key_access.slos", fmt.Sprintf("%v", metadata.APIKeyAccess.SLOs)),
+					resource.TestCheckResourceAttr("data.honeycombio_auth_metadata.test", "api_key_access.triggers", fmt.Sprintf("%v", metadata.APIKeyAccess.Triggers)),
+				),
+			},
+		},
+	})
+}
diff --git a/internal/provider/trigger_resource_test.go b/internal/provider/trigger_resource_test.go
index fef1de5..b0a20d3 100644
--- a/internal/provider/trigger_resource_test.go
+++ b/internal/provider/trigger_resource_test.go
@@ -26,6 +26,7 @@ func TestAcc_TriggerResource(t *testing.T) {
 					resource.TestCheckResourceAttr("honeycombio_trigger.test", "name", "Test trigger from terraform-provider-honeycombio"),
 					resource.TestCheckResourceAttr("honeycombio_trigger.test", "frequency", "600"),
 					resource.TestCheckResourceAttr("honeycombio_trigger.test", "recipient.#", "2"),
+					resource.TestCheckResourceAttr("honeycombio_trigger.test", "threshold.0.exceeded_limit", "1"),
 					resource.TestCheckResourceAttrPair("honeycombio_trigger.test", "query_id", "honeycombio_query.test", "id"),
 				),
 			},
@@ -115,12 +116,18 @@ func TestAcc_TriggerResourceUpdateRecipientByID(t *testing.T) {
 		ProtoV5ProviderFactories: testAccProtoV5MuxServerFactory,
 		Steps: []resource.TestStep{
 			{
-				Config: testAccConfigTriggerRecipientByID(dataset, testRecipients[0].ID),
-				Check:  resource.TestCheckResourceAttr("honeycombio_trigger.test", "recipient.0.id", testRecipients[0].ID),
+				Config: testAccConfigTriggerRecipientByID(dataset, testRecipients[0].ID, 3),
+				Check: resource.ComposeAggregateTestCheckFunc(
+					resource.TestCheckResourceAttr("honeycombio_trigger.test", "recipient.0.id", testRecipients[0].ID),
+					resource.TestCheckResourceAttr("honeycombio_trigger.test", "threshold.0.exceeded_limit", "3"),
+				),
 			},
 			{
-				Config: testAccConfigTriggerRecipientByID(dataset, testRecipients[1].ID),
-				Check:  resource.TestCheckResourceAttr("honeycombio_trigger.test", "recipient.0.id", testRecipients[1].ID),
+				Config: testAccConfigTriggerRecipientByID(dataset, testRecipients[1].ID, 5),
+				Check: resource.ComposeAggregateTestCheckFunc(
+					resource.TestCheckResourceAttr("honeycombio_trigger.test", "recipient.0.id", testRecipients[1].ID),
+					resource.TestCheckResourceAttr("honeycombio_trigger.test", "threshold.0.exceeded_limit", "5"),
+				),
 			},
 		},
 	})
@@ -453,7 +460,7 @@ resource "honeycombio_trigger" "test" {
 }`, dataset, pdseverity)
 }
 
-func testAccConfigTriggerRecipientByID(dataset, recipientID string) string {
+func testAccConfigTriggerRecipientByID(dataset, recipientID string, exceededLimit int) string {
 	return fmt.Sprintf(`
 data "honeycombio_query_specification" "test" {
   calculation {
@@ -473,15 +480,17 @@ resource "honeycombio_trigger" "test" {
   name    = "Test trigger with changing recipient id"
   dataset = "%[1]s"
   query_id = honeycombio_query.test.id
+
   threshold {
-    op    = ">"
-    value = 100
+    op             = ">"
+    value          = 100
+    exceeded_limit = %[3]d
   }
 
   recipient {
     id = "%[2]s"
   }
-}`, dataset, recipientID)
+}`, dataset, recipientID, exceededLimit)
 }
 
 func testAccEnsureTriggerExists(t *testing.T, name string) resource.TestCheckFunc {