-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tailscale: add support for S3 logstreaming
We recently added support for S3 logstreaming endpoints to our API. This involved adding several new fields to the LogstreamConfiguration resource (available when the new "s3" destinationType is used), plus a new AWSExternalID resource needed when AWS role-based authentication is used for an S3 logstreaming endpoint. This commit updates the Terraform provider to reflect these changes. We add support for the new fields to the tailscale_logstream_configuration resource, and create a new tailscale_aws_external_id resource. Unfortunately, we have to add some special handling for the "user" field of tailscale_logstream_configuration. Previously, we specified a default dummy value of "user" for the "user" field. Now, the PUT LogstreamConfiguration endpoint does not allow any value to be specified for a s3 destinationType. We could loosen that restriction at the API layer, but I would prefer not to do that. However, we don't want to break any existing users who are relying on this default and have it in their Terraform state. So the approach I've chosen is to add special handling for the "user" field: - When creating or updating a configuration, if destinationType == "s3" and user == "user", we assume the "user" value came from the default and we do not actually send it to the API. - When reading or importing a configuration, if destinationType == "s3" and there is no user field (which should always be true), we set user = "user" in the Terraform state. Fixes #458 Fixes tailscale/corp#24533 Signed-off-by: Zach Hauser <[email protected]>
- Loading branch information
Showing
11 changed files
with
513 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
--- | ||
# generated by https://github.com/hashicorp/terraform-plugin-docs | ||
page_title: "tailscale_aws_external_id Resource - terraform-provider-tailscale" | ||
subcategory: "" | ||
description: |- | ||
The aws_external_id resource allows you to mint an AWS External ID that Tailscale can use to assume an AWS IAM role that you create for the purposes of allowing Tailscale to stream logs to your S3 bucket. See the logstream_configuration resource for more details. | ||
--- | ||
|
||
# tailscale_aws_external_id (Resource) | ||
|
||
The aws_external_id resource allows you to mint an AWS External ID that Tailscale can use to assume an AWS IAM role that you create for the purposes of allowing Tailscale to stream logs to your S3 bucket. See the logstream_configuration resource for more details. | ||
|
||
## Example Usage | ||
|
||
```terraform | ||
resource "tailscale_aws_external_id" "prod" {} | ||
resource "tailscale_logstream_configuration" "configuration_logs" { | ||
log_type = "configuration" | ||
destination_type = "s3" | ||
s3_bucket = aws_s3_bucket.tailscale_logs.id | ||
s3_region = "us-west-2" | ||
s3_authentication_type = "rolearn" | ||
s3_role_arn = aws_iam_role.logs_writer.arn | ||
s3_external_id = tailscale_aws_external_id.prod.external_id | ||
} | ||
resource "aws_iam_role" "logs_writer" { | ||
name = "logs-writer" | ||
assume_role_policy = data.aws_iam_policy_document.tailscale_assume_role.json | ||
} | ||
resource "aws_iam_role_policy" "logs_writer" { | ||
role = aws_iam_role.logs_writer.id | ||
policy = data.aws_iam_policy_document.logs_writer.json | ||
} | ||
data "aws_iam_policy_document" "tailscale_assume_role" { | ||
statement { | ||
actions = ["sts:AssumeRole"] | ||
principals { | ||
type = "AWS" | ||
identifiers = [tailscale_aws_external_id.prod.tailscale_aws_account_id] | ||
} | ||
condition { | ||
test = "StringEquals" | ||
variable = "sts:ExternalId" | ||
values = [tailscale_aws_external_id.prod.external_id] | ||
} | ||
} | ||
} | ||
data "aws_iam_policy_document" "logs_writer" { | ||
statement { | ||
effect = "Allow" | ||
actions = ["s3:*"] | ||
resources = [ | ||
"arn:aws:s3:::example-bucket", | ||
"arn:aws:s3:::example-bucket/*" | ||
] | ||
} | ||
} | ||
``` | ||
|
||
<!-- schema generated by tfplugindocs --> | ||
## Schema | ||
|
||
### Read-Only | ||
|
||
- `external_id` (String) The External ID that Tailscale will supply when assuming your role. You must reference this in your IAM role's trust policy. See https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_common-scenarios_third-party.html for more information on external IDs. | ||
- `id` (String) The ID of this resource. | ||
- `tailscale_aws_account_id` (String) The AWS account from which Tailscale will assume your role. You must reference this in your IAM role's trust policy. See https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_common-scenarios_third-party.html for more information on external IDs. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
resource "tailscale_aws_external_id" "prod" {} | ||
|
||
resource "tailscale_logstream_configuration" "configuration_logs" { | ||
log_type = "configuration" | ||
destination_type = "s3" | ||
s3_bucket = aws_s3_bucket.tailscale_logs.id | ||
s3_region = "us-west-2" | ||
s3_authentication_type = "rolearn" | ||
s3_role_arn = aws_iam_role.logs_writer.arn | ||
s3_external_id = tailscale_aws_external_id.prod.external_id | ||
} | ||
|
||
resource "aws_iam_role" "logs_writer" { | ||
name = "logs-writer" | ||
assume_role_policy = data.aws_iam_policy_document.tailscale_assume_role.json | ||
} | ||
|
||
resource "aws_iam_role_policy" "logs_writer" { | ||
role = aws_iam_role.logs_writer.id | ||
policy = data.aws_iam_policy_document.logs_writer.json | ||
} | ||
|
||
data "aws_iam_policy_document" "tailscale_assume_role" { | ||
statement { | ||
actions = ["sts:AssumeRole"] | ||
principals { | ||
type = "AWS" | ||
identifiers = [tailscale_aws_external_id.prod.tailscale_aws_account_id] | ||
} | ||
condition { | ||
test = "StringEquals" | ||
variable = "sts:ExternalId" | ||
values = [tailscale_aws_external_id.prod.external_id] | ||
} | ||
} | ||
} | ||
|
||
data "aws_iam_policy_document" "logs_writer" { | ||
statement { | ||
effect = "Allow" | ||
actions = ["s3:*"] | ||
resources = [ | ||
"arn:aws:s3:::example-bucket", | ||
"arn:aws:s3:::example-bucket/*" | ||
] | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
examples/resources/tailscale_logstream_configuration/resource.tf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,33 @@ | ||
# Example configuration for a non-S3 logstreaming endpoint | ||
|
||
resource "tailscale_logstream_configuration" "sample_logstream_configuration" { | ||
log_type = "configuration" | ||
destination_type = "panther" | ||
url = "https://example.com" | ||
token = "some-token" | ||
} | ||
|
||
# Example configuration for an AWS S3 logstreaming endpoint | ||
|
||
resource "tailscale_logstream_configuration" "sample_logstream_configuration_s3" { | ||
log_type = "configuration" | ||
destination_type = "s3" | ||
s3_bucket = aws_s3_bucket.tailscale_logs.id | ||
s3_region = "us-west-2" | ||
s3_authentication_type = "rolearn" | ||
s3_role_arn = aws_iam_role.tailscale_logs_writer.arn | ||
s3_external_id = tailscale_aws_external_id.prod.external_id | ||
} | ||
|
||
# Example configuration for an S3-compatible logstreaming endpoint | ||
|
||
resource "tailscale_logstream_configuration" "sample_logstream_configuration_s3_compatible" { | ||
log_type = "configuration" | ||
destination_type = "s3" | ||
url = "https://s3.example.com" | ||
s3_bucket = "example-bucket" | ||
s3_region = "us-west-2" | ||
s3_authentication_type = "accesskey" | ||
s3_access_key_id = "some-access-key" | ||
s3_secret_access_key = "some-secret-key" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright (c) David Bond, Tailscale Inc, & Contributors | ||
// SPDX-License-Identifier: MIT | ||
|
||
package tailscale | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
|
||
tsclient "github.com/tailscale/tailscale-client-go/v2" | ||
) | ||
|
||
func resourceAWSExternalID() *schema.Resource { | ||
return &schema.Resource{ | ||
Description: "The aws_external_id resource allows you to mint an AWS External ID that Tailscale can use to assume an AWS IAM role that you create for the purposes of allowing Tailscale to stream logs to your S3 bucket. See the logstream_configuration resource for more details.", | ||
CreateContext: resourceAWSExternalIDCreate, | ||
|
||
// No GET or DELETE endpoints in the API. This is a create-only resource. | ||
ReadContext: schema.NoopContext, | ||
DeleteContext: schema.NoopContext, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"external_id": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
Description: "The External ID that Tailscale will supply when assuming your role. You must reference this in your IAM role's trust policy. See https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_common-scenarios_third-party.html for more information on external IDs.", | ||
}, | ||
"tailscale_aws_account_id": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
Description: "The AWS account from which Tailscale will assume your role. You must reference this in your IAM role's trust policy. See https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_common-scenarios_third-party.html for more information on external IDs.", | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceAWSExternalIDCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { | ||
client := m.(*tsclient.Client) | ||
|
||
// We pass "reusable: false" on purpose. Otherwise, two tailscale_aws_external_id resources | ||
// could end up with the same resource ID (because we use the actual external ID). | ||
// | ||
// Also, "reusable: true" is an optimization intended for the admin console UI's usage | ||
// pattern, and it's not really necessary for Terraform use cases. | ||
aid, err := client.Logging().CreateOrGetAwsExternalId(ctx, false) | ||
if err != nil { | ||
return diagnosticsError(err, "Failed to create AWS External ID") | ||
} | ||
|
||
d.SetId(aid.ExternalID) | ||
if err = d.Set("external_id", aid.ExternalID); err != nil { | ||
return diagnosticsError(err, "Failed to set externalId") | ||
} | ||
if err = d.Set("tailscale_aws_account_id", aid.TailscaleAWSAccountID); err != nil { | ||
return diagnosticsError(err, "Failed to set AWSAccountID") | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// Copyright (c) David Bond, Tailscale Inc, & Contributors | ||
// SPDX-License-Identifier: MIT | ||
|
||
package tailscale | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
) | ||
|
||
const testAWSExternalID = ` | ||
resource "tailscale_aws_external_id" "test" {} | ||
` | ||
|
||
func TestAccTailscaleAWSExternalID(t *testing.T) { | ||
const resourceName = "tailscale_aws_external_id.test" | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
ProviderFactories: testAccProviderFactories(t), | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAWSExternalID, | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttrSet(resourceName, "external_id"), | ||
resource.TestCheckResourceAttrSet(resourceName, "tailscale_aws_account_id"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} |
Oops, something went wrong.