diff --git a/.hashibot.hcl b/.hashibot.hcl index 8e10d40b9127..902dd13f484d 100644 --- a/.hashibot.hcl +++ b/.hashibot.hcl @@ -529,6 +529,9 @@ behavior "regexp_issue_labeler_v2" "service_labels" { "service/shield" = [ "aws_shield_", ], + "service/signer" = [ + "aws_signer_", + ], "service/simpledb" = [ "aws_simpledb_", ], @@ -1408,6 +1411,10 @@ behavior "pull_request_path_labeler" "service_labels" { "**/*_shield_*", "**/shield_*", ], + "service/signer" = [ + "**/*_signer_*", + "**/signer_*" + ] "service/simpledb" = [ "aws/internal/service/simpledb/**/*", "**/*_simpledb_*", diff --git a/aws/config.go b/aws/config.go index 793d4a3a28ec..800cbba3427c 100644 --- a/aws/config.go +++ b/aws/config.go @@ -139,6 +139,7 @@ import ( "github.com/aws/aws-sdk-go/service/ses" "github.com/aws/aws-sdk-go/service/sfn" "github.com/aws/aws-sdk-go/service/shield" + "github.com/aws/aws-sdk-go/service/signer" "github.com/aws/aws-sdk-go/service/simpledb" "github.com/aws/aws-sdk-go/service/sns" "github.com/aws/aws-sdk-go/service/sqs" @@ -335,6 +336,7 @@ type AWSClient struct { sesconn *ses.SES sfnconn *sfn.SFN shieldconn *shield.Shield + signerconn *signer.Signer simpledbconn *simpledb.SimpleDB snsconn *sns.SNS sqsconn *sqs.SQS @@ -564,6 +566,7 @@ func (c *Config) Client() (interface{}, error) { servicequotasconn: servicequotas.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["servicequotas"])})), sesconn: ses.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["ses"])})), sfnconn: sfn.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["stepfunctions"])})), + signerconn: signer.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["signer"])})), simpledbconn: simpledb.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["sdb"])})), snsconn: sns.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["sns"])})), sqsconn: sqs.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["sqs"])})), diff --git a/aws/data_source_aws_signer_signing_job.go b/aws/data_source_aws_signer_signing_job.go new file mode 100644 index 000000000000..f916237c143e --- /dev/null +++ b/aws/data_source_aws_signer_signing_job.go @@ -0,0 +1,224 @@ +package aws + +import ( + "fmt" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/signer" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceAwsSignerSigningJob() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsSignerSigningJobRead, + + Schema: map[string]*schema.Schema{ + "job_id": { + Type: schema.TypeString, + Required: true, + }, + "completed_at": { + Type: schema.TypeString, + Computed: true, + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + }, + "job_owner": { + Type: schema.TypeString, + Computed: true, + }, + "job_invoker": { + Type: schema.TypeString, + Computed: true, + }, + "platform_display_name": { + Type: schema.TypeString, + Computed: true, + }, + "platform_id": { + Type: schema.TypeString, + Computed: true, + }, + "profile_name": { + Type: schema.TypeString, + Computed: true, + }, + "profile_version": { + Type: schema.TypeString, + Computed: true, + }, + "requested_by": { + Type: schema.TypeString, + Computed: true, + }, + "revocation_record": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "reason": { + Type: schema.TypeString, + Computed: true, + }, + "revoked_at": { + Type: schema.TypeString, + Computed: true, + }, + "revoked_by": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "signature_expires_at": { + Type: schema.TypeString, + Computed: true, + }, + "signed_object": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "s3": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bucket": { + Type: schema.TypeString, + Computed: true, + }, + "key": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "source": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "s3": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bucket": { + Type: schema.TypeString, + Computed: true, + }, + "key": { + Type: schema.TypeString, + Computed: true, + }, + "version": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "status_reason": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAwsSignerSigningJobRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).signerconn + jobId := d.Get("job_id").(string) + + describeSigningJobOutput, err := conn.DescribeSigningJob(&signer.DescribeSigningJobInput{ + JobId: aws.String(jobId), + }) + + if err != nil { + return fmt.Errorf("error reading Signer signing job (%s): %s", d.Id(), err) + } + + if err := d.Set("completed_at", aws.TimeValue(describeSigningJobOutput.CompletedAt).Format(time.RFC3339)); err != nil { + return fmt.Errorf("error setting signer signing job completed at: %s", err) + } + + if err := d.Set("created_at", aws.TimeValue(describeSigningJobOutput.CreatedAt).Format(time.RFC3339)); err != nil { + return fmt.Errorf("error setting signer signing job created at: %s", err) + } + + if err := d.Set("job_invoker", describeSigningJobOutput.JobInvoker); err != nil { + return fmt.Errorf("error setting signer signing job invoker: %s", err) + } + + if err := d.Set("job_owner", describeSigningJobOutput.JobOwner); err != nil { + return fmt.Errorf("error setting signer signing job owner: %s", err) + } + + if err := d.Set("platform_display_name", describeSigningJobOutput.PlatformDisplayName); err != nil { + return fmt.Errorf("error setting signer signing job platform display name: %s", err) + } + + if err := d.Set("platform_id", describeSigningJobOutput.PlatformId); err != nil { + return fmt.Errorf("error setting signer signing job platform id: %s", err) + } + + if err := d.Set("profile_name", describeSigningJobOutput.ProfileName); err != nil { + return fmt.Errorf("error setting signer signing job profile name: %s", err) + } + + if err := d.Set("profile_version", describeSigningJobOutput.ProfileVersion); err != nil { + return fmt.Errorf("error setting signer signing job profile version: %s", err) + } + + if err := d.Set("requested_by", describeSigningJobOutput.RequestedBy); err != nil { + return fmt.Errorf("error setting signer signing job requested by: %s", err) + } + + if err := d.Set("revocation_record", flattenSignerSigningJobRevocationRecord(describeSigningJobOutput.RevocationRecord)); err != nil { + return fmt.Errorf("error setting signer signing job revocation record: %s", err) + } + + signatureExpiresAt := "" + if describeSigningJobOutput.SignatureExpiresAt != nil { + signatureExpiresAt = aws.TimeValue(describeSigningJobOutput.SignatureExpiresAt).Format(time.RFC3339) + } + if err := d.Set("signature_expires_at", signatureExpiresAt); err != nil { + return fmt.Errorf("error setting signer signing job requested by: %s", err) + } + + if err := d.Set("signed_object", flattenSignerSigningJobSignedObject(describeSigningJobOutput.SignedObject)); err != nil { + return fmt.Errorf("error setting signer signing job signed object: %s", err) + } + + if err := d.Set("source", flattenSignerSigningJobSource(describeSigningJobOutput.Source)); err != nil { + return fmt.Errorf("error setting signer signing job source: %s", err) + } + + if err := d.Set("status", describeSigningJobOutput.Status); err != nil { + return fmt.Errorf("error setting signer signing job status: %s", err) + } + + if err := d.Set("status_reason", describeSigningJobOutput.StatusReason); err != nil { + return fmt.Errorf("error setting signer signing job status reason: %s", err) + } + + d.SetId(aws.StringValue(describeSigningJobOutput.JobId)) + + return nil +} diff --git a/aws/data_source_aws_signer_signing_job_test.go b/aws/data_source_aws_signer_signing_job_test.go new file mode 100644 index 000000000000..3dea101ad407 --- /dev/null +++ b/aws/data_source_aws_signer_signing_job_test.go @@ -0,0 +1,85 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceAWSSignerSigningJob_basic(t *testing.T) { + rString := acctest.RandString(48) + profileName := fmt.Sprintf("tf_acc_sp_basic_%s", rString) + dataSourceName := "data.aws_signer_signing_job.test" + resourceName := "aws_signer_signing_job.job_test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAWSSignerSigningJobConfigBasic(profileName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(dataSourceName, "status", resourceName, "status"), + resource.TestCheckResourceAttrPair(dataSourceName, "job_owner", resourceName, "job_owner"), + resource.TestCheckResourceAttrPair(dataSourceName, "job_invoker", resourceName, "job_invoker"), + resource.TestCheckResourceAttrPair(dataSourceName, "profile_name", resourceName, "profile_name"), + ), + }, + }, + }) +} + +func testAccDataSourceAWSSignerSigningJobConfigBasic(profileName string) string { + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} + +resource "aws_signer_signing_profile" "test_sp" { + platform_id = "AWSLambda-SHA384-ECDSA" + name = "%s" +} + +resource "aws_s3_bucket" "bucket" { + bucket = "tf-signer-signing-bucket" + + versioning { + enabled = true + } + + force_destroy = true +} + +resource "aws_s3_bucket" "dest_bucket" { + bucket = "tf-signer-signing-dest-bucket" + force_destroy = true +} + +resource "aws_s3_bucket_object" "lambda_signing_code" { + bucket = aws_s3_bucket.bucket.bucket + key = "lambdatest.zip" + source = "test-fixtures/lambdatest.zip" +} + +resource "aws_signer_signing_job" "job_test" { + profile_name = aws_signer_signing_profile.test_sp.name + + source { + s3 { + bucket = aws_s3_bucket.bucket.bucket + key = aws_s3_bucket_object.lambda_signing_code.key + version = aws_s3_bucket_object.lambda_signing_code.version_id + } + } + + destination { + s3 { + bucket = aws_s3_bucket.dest_bucket.bucket + } + } +} + +data "aws_signer_signing_job" "test" { + job_id = aws_signer_signing_job.job_test.job_id +}`, profileName) +} diff --git a/aws/data_source_aws_signer_signing_profile.go b/aws/data_source_aws_signer_signing_profile.go new file mode 100644 index 000000000000..ffacc2810883 --- /dev/null +++ b/aws/data_source_aws_signer_signing_profile.go @@ -0,0 +1,143 @@ +package aws + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/signer" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" +) + +func dataSourceAwsSignerSigningProfile() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsSignerSigningProfileRead, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "platform_display_name": { + Type: schema.TypeString, + Computed: true, + }, + "platform_id": { + Type: schema.TypeString, + Computed: true, + }, + "revocation_record": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "revocation_effective_from": { + Type: schema.TypeString, + Computed: true, + }, + "revoked_at": { + Type: schema.TypeString, + Computed: true, + }, + "revoked_by": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "signature_validity_period": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeInt, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "tags": tagsSchemaComputed(), + "version": { + Type: schema.TypeString, + Computed: true, + }, + "version_arn": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAwsSignerSigningProfileRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).signerconn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + profileName := d.Get("name").(string) + signingProfileOutput, err := conn.GetSigningProfile(&signer.GetSigningProfileInput{ + ProfileName: aws.String(profileName), + }) + + if err != nil { + return fmt.Errorf("error reading Signer signing profile (%s): %s", d.Id(), err) + } + + if err := d.Set("platform_id", signingProfileOutput.PlatformId); err != nil { + return fmt.Errorf("error setting signer signing profile platform id: %s", err) + } + + if err := d.Set("signature_validity_period", []interface{}{ + map[string]interface{}{ + "value": signingProfileOutput.SignatureValidityPeriod.Value, + "type": signingProfileOutput.SignatureValidityPeriod.Type, + }, + }); err != nil { + return fmt.Errorf("error setting signer signing profile signature validity period: %s", err) + } + + if err := d.Set("platform_display_name", signingProfileOutput.PlatformDisplayName); err != nil { + return fmt.Errorf("error setting signer signing profile platform display name: %s", err) + } + + if err := d.Set("arn", signingProfileOutput.Arn); err != nil { + return fmt.Errorf("error setting signer signing profile arn: %s", err) + } + + if err := d.Set("version", signingProfileOutput.ProfileVersion); err != nil { + return fmt.Errorf("error setting signer signing profile version: %s", err) + } + + if err := d.Set("version_arn", signingProfileOutput.ProfileVersionArn); err != nil { + return fmt.Errorf("error setting signer signing profile version arn: %s", err) + } + + if err := d.Set("status", signingProfileOutput.Status); err != nil { + return fmt.Errorf("error setting signer signing profile status: %s", err) + } + + if err := d.Set("tags", keyvaluetags.SignerKeyValueTags(signingProfileOutput.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting signer signing profile tags: %s", err) + } + + if err := d.Set("revocation_record", flattenSignerSigningProfileRevocationRecord(signingProfileOutput.RevocationRecord)); err != nil { + return fmt.Errorf("error setting signer signing profile revocation record: %s", err) + } + + d.SetId(aws.StringValue(signingProfileOutput.ProfileName)) + + return nil +} diff --git a/aws/data_source_aws_signer_signing_profile_test.go b/aws/data_source_aws_signer_signing_profile_test.go new file mode 100644 index 000000000000..a440cbbf2438 --- /dev/null +++ b/aws/data_source_aws_signer_signing_profile_test.go @@ -0,0 +1,48 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceAWSSignerSigningProfile_basic(t *testing.T) { + dataSourceName := "data.aws_signer_signing_profile.test" + resourceName := "aws_signer_signing_profile.test" + rString := acctest.RandString(48) + profileName := fmt.Sprintf("tf_acc_sp_basic_%s", rString) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAWSSignerSigningProfileConfigBasic(profileName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(dataSourceName, "name", resourceName, "name"), + resource.TestCheckResourceAttrPair(dataSourceName, "platform_id", resourceName, "platform_id"), + resource.TestCheckResourceAttrPair(dataSourceName, "signature_validity_period.value", resourceName, "signature_validity_period.value"), + resource.TestCheckResourceAttrPair(dataSourceName, "signature_validity_period.type", resourceName, "signature_validity_period.type"), + resource.TestCheckResourceAttrPair(dataSourceName, "platform_display_name", resourceName, "platform_display_name"), + resource.TestCheckResourceAttrPair(dataSourceName, "status", resourceName, "status"), + resource.TestCheckResourceAttrPair(dataSourceName, "tags", resourceName, "tags"), + resource.TestCheckResourceAttrPair(dataSourceName, "arn", resourceName, "arn"), + ), + }, + }, + }) +} + +func testAccDataSourceAWSSignerSigningProfileConfigBasic(profileName string) string { + return fmt.Sprintf(` +resource "aws_signer_signing_profile" "test" { + platform_id = "AWSLambda-SHA384-ECDSA" + name = "%s" +} + +data "aws_signer_signing_profile" "test" { + name = aws_signer_signing_profile.test.name +}`, profileName) +} diff --git a/aws/internal/keyvaluetags/generators/listtags/main.go b/aws/internal/keyvaluetags/generators/listtags/main.go index ece382df3c94..b27a56d9e974 100644 --- a/aws/internal/keyvaluetags/generators/listtags/main.go +++ b/aws/internal/keyvaluetags/generators/listtags/main.go @@ -106,6 +106,7 @@ var serviceNames = []string{ "securityhub", "servicediscovery", "sfn", + "signer", "sns", "sqs", "ssm", diff --git a/aws/internal/keyvaluetags/generators/servicetags/main.go b/aws/internal/keyvaluetags/generators/servicetags/main.go index fde36cda2dc0..d983f32c7dae 100644 --- a/aws/internal/keyvaluetags/generators/servicetags/main.go +++ b/aws/internal/keyvaluetags/generators/servicetags/main.go @@ -136,6 +136,7 @@ var mapServiceNames = []string{ "pinpoint", "resourcegroups", "securityhub", + "signer", "sqs", "synthetics", "worklink", diff --git a/aws/internal/keyvaluetags/generators/updatetags/main.go b/aws/internal/keyvaluetags/generators/updatetags/main.go index 452628f437b8..fca2fad880a3 100644 --- a/aws/internal/keyvaluetags/generators/updatetags/main.go +++ b/aws/internal/keyvaluetags/generators/updatetags/main.go @@ -112,6 +112,7 @@ var serviceNames = []string{ "securityhub", "servicediscovery", "sfn", + "signer", "sns", "sqs", "ssm", diff --git a/aws/internal/keyvaluetags/list_tags_gen.go b/aws/internal/keyvaluetags/list_tags_gen.go index 359cd2f9e698..5d4ab407a0f6 100644 --- a/aws/internal/keyvaluetags/list_tags_gen.go +++ b/aws/internal/keyvaluetags/list_tags_gen.go @@ -93,6 +93,7 @@ import ( "github.com/aws/aws-sdk-go/service/securityhub" "github.com/aws/aws-sdk-go/service/servicediscovery" "github.com/aws/aws-sdk-go/service/sfn" + "github.com/aws/aws-sdk-go/service/signer" "github.com/aws/aws-sdk-go/service/sns" "github.com/aws/aws-sdk-go/service/sqs" "github.com/aws/aws-sdk-go/service/ssm" @@ -1631,6 +1632,23 @@ func SfnListTags(conn *sfn.SFN, identifier string) (KeyValueTags, error) { return SfnKeyValueTags(output.Tags), nil } +// SignerListTags lists signer service tags. +// The identifier is typically the Amazon Resource Name (ARN), although +// it may also be a different identifier depending on the service. +func SignerListTags(conn *signer.Signer, identifier string) (KeyValueTags, error) { + input := &signer.ListTagsForResourceInput{ + ResourceArn: aws.String(identifier), + } + + output, err := conn.ListTagsForResource(input) + + if err != nil { + return New(nil), err + } + + return SignerKeyValueTags(output.Tags), nil +} + // SnsListTags lists sns service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. diff --git a/aws/internal/keyvaluetags/service_generation_customizations.go b/aws/internal/keyvaluetags/service_generation_customizations.go index 98a5b9b75baf..faf2db2fe522 100644 --- a/aws/internal/keyvaluetags/service_generation_customizations.go +++ b/aws/internal/keyvaluetags/service_generation_customizations.go @@ -103,6 +103,7 @@ import ( "github.com/aws/aws-sdk-go/service/securityhub" "github.com/aws/aws-sdk-go/service/servicediscovery" "github.com/aws/aws-sdk-go/service/sfn" + "github.com/aws/aws-sdk-go/service/signer" "github.com/aws/aws-sdk-go/service/sns" "github.com/aws/aws-sdk-go/service/sqs" "github.com/aws/aws-sdk-go/service/ssm" @@ -319,6 +320,8 @@ func ServiceClientType(serviceName string) string { funcType = reflect.TypeOf(servicediscovery.New) case "sfn": funcType = reflect.TypeOf(sfn.New) + case "signer": + funcType = reflect.TypeOf(signer.New) case "sns": funcType = reflect.TypeOf(sns.New) case "sqs": diff --git a/aws/internal/keyvaluetags/service_tags_gen.go b/aws/internal/keyvaluetags/service_tags_gen.go index c6c0cadaddba..82c4194bd1f0 100644 --- a/aws/internal/keyvaluetags/service_tags_gen.go +++ b/aws/internal/keyvaluetags/service_tags_gen.go @@ -431,6 +431,16 @@ func SecurityhubKeyValueTags(tags map[string]*string) KeyValueTags { return New(tags) } +// SignerTags returns signer service tags. +func (tags KeyValueTags) SignerTags() map[string]*string { + return aws.StringMap(tags.Map()) +} + +// SignerKeyValueTags creates KeyValueTags from signer service tags. +func SignerKeyValueTags(tags map[string]*string) KeyValueTags { + return New(tags) +} + // SqsTags returns sqs service tags. func (tags KeyValueTags) SqsTags() map[string]*string { return aws.StringMap(tags.Map()) diff --git a/aws/internal/keyvaluetags/update_tags_gen.go b/aws/internal/keyvaluetags/update_tags_gen.go index 8846f8770ca8..fffb6b61892c 100644 --- a/aws/internal/keyvaluetags/update_tags_gen.go +++ b/aws/internal/keyvaluetags/update_tags_gen.go @@ -101,6 +101,7 @@ import ( "github.com/aws/aws-sdk-go/service/securityhub" "github.com/aws/aws-sdk-go/service/servicediscovery" "github.com/aws/aws-sdk-go/service/sfn" + "github.com/aws/aws-sdk-go/service/signer" "github.com/aws/aws-sdk-go/service/sns" "github.com/aws/aws-sdk-go/service/sqs" "github.com/aws/aws-sdk-go/service/ssm" @@ -3537,6 +3538,42 @@ func SfnUpdateTags(conn *sfn.SFN, identifier string, oldTagsMap interface{}, new return nil } +// SignerUpdateTags updates signer service tags. +// The identifier is typically the Amazon Resource Name (ARN), although +// it may also be a different identifier depending on the service. +func SignerUpdateTags(conn *signer.Signer, identifier string, oldTagsMap interface{}, newTagsMap interface{}) error { + oldTags := New(oldTagsMap) + newTags := New(newTagsMap) + + if removedTags := oldTags.Removed(newTags); len(removedTags) > 0 { + input := &signer.UntagResourceInput{ + ResourceArn: aws.String(identifier), + TagKeys: aws.StringSlice(removedTags.IgnoreAws().Keys()), + } + + _, err := conn.UntagResource(input) + + if err != nil { + return fmt.Errorf("error untagging resource (%s): %w", identifier, err) + } + } + + if updatedTags := oldTags.Updated(newTags); len(updatedTags) > 0 { + input := &signer.TagResourceInput{ + ResourceArn: aws.String(identifier), + Tags: updatedTags.IgnoreAws().SignerTags(), + } + + _, err := conn.TagResource(input) + + if err != nil { + return fmt.Errorf("error tagging resource (%s): %w", identifier, err) + } + } + + return nil +} + // SnsUpdateTags updates sns service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. diff --git a/aws/provider.go b/aws/provider.go index 8fd45b6577b3..c912fe9b05ee 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -340,6 +340,8 @@ func Provider() *schema.Provider { "aws_servicequotas_service_quota": dataSourceAwsServiceQuotasServiceQuota(), "aws_sfn_activity": dataSourceAwsSfnActivity(), "aws_sfn_state_machine": dataSourceAwsSfnStateMachine(), + "aws_signer_signing_job": dataSourceAwsSignerSigningJob(), + "aws_signer_signing_profile": dataSourceAwsSignerSigningProfile(), "aws_sns_topic": dataSourceAwsSnsTopic(), "aws_sqs_queue": dataSourceAwsSqsQueue(), "aws_ssm_document": dataSourceAwsSsmDocument(), @@ -893,6 +895,9 @@ func Provider() *schema.Provider { "aws_service_discovery_service": resourceAwsServiceDiscoveryService(), "aws_servicequotas_service_quota": resourceAwsServiceQuotasServiceQuota(), "aws_shield_protection": resourceAwsShieldProtection(), + "aws_signer_signing_job": resourceAwsSignerSigningJob(), + "aws_signer_signing_profile": resourceAwsSignerSigningProfile(), + "aws_signer_signing_profile_permission": resourceAwsSignerSigningProfilePermission(), "aws_simpledb_domain": resourceAwsSimpleDBDomain(), "aws_ssm_activation": resourceAwsSsmActivation(), "aws_ssm_association": resourceAwsSsmAssociation(), @@ -1220,6 +1225,7 @@ func init() { "servicequotas", "ses", "shield", + "signer", "sns", "sqs", "ssm", diff --git a/aws/resource_aws_signer_signing_job.go b/aws/resource_aws_signer_signing_job.go new file mode 100644 index 000000000000..e77082c0ca04 --- /dev/null +++ b/aws/resource_aws_signer_signing_job.go @@ -0,0 +1,491 @@ +package aws + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/signer" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceAwsSignerSigningJob() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsSignerSigningJobCreate, + Read: resourceAwsSignerSigningJobRead, + Delete: schema.Noop, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "profile_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "source": { + Type: schema.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "s3": { + Type: schema.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bucket": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "key": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "version": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + }, + }, + }, + }, + }, + "destination": { + Type: schema.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "s3": { + Type: schema.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bucket": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "prefix": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + }, + }, + }, + }, + }, + "ignore_signing_job_failure": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, + "completed_at": { + Type: schema.TypeString, + Computed: true, + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + }, + "job_id": { + Type: schema.TypeString, + Computed: true, + }, + "job_invoker": { + Type: schema.TypeString, + Computed: true, + }, + "job_owner": { + Type: schema.TypeString, + Computed: true, + }, + "platform_display_name": { + Type: schema.TypeString, + Computed: true, + }, + "platform_id": { + Type: schema.TypeString, + Computed: true, + }, + "profile_version": { + Type: schema.TypeString, + Computed: true, + }, + "requested_by": { + Type: schema.TypeString, + Computed: true, + }, + "revocation_record": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "reason": { + Type: schema.TypeString, + Computed: true, + }, + "revoked_at": { + Type: schema.TypeString, + Computed: true, + }, + "revoked_by": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "signature_expires_at": { + Type: schema.TypeString, + Computed: true, + }, + "signed_object": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "s3": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bucket": { + Type: schema.TypeString, + Computed: true, + }, + "key": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "status_reason": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsSignerSigningJobCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).signerconn + profileName := d.Get("profile_name") + source := d.Get("source").([]interface{}) + destination := d.Get("destination").([]interface{}) + + startSigningJobInput := &signer.StartSigningJobInput{ + ProfileName: aws.String(profileName.(string)), + Source: expandSignerSigningJobSource(source), + Destination: expandSignerSigningJobDestination(destination), + } + + log.Printf("[DEBUG] Starting Signer Signing Job using profile name %q.", profileName) + startSigningJobOutput, err := conn.StartSigningJob(startSigningJobInput) + if err != nil { + return fmt.Errorf("error starting Signing Job: %w", err) + } + + jobId := aws.StringValue(startSigningJobOutput.JobId) + + ignoreSigningJobFailure := d.Get("ignore_signing_job_failure").(bool) + log.Printf("[DEBUG] Waiting for Signer Signing Job ID (%s) to complete.", jobId) + waiterError := conn.WaitUntilSuccessfulSigningJobWithContext(aws.BackgroundContext(), &signer.DescribeSigningJobInput{ + JobId: aws.String(jobId), + }, request.WithWaiterMaxAttempts(200), request.WithWaiterDelay(request.ConstantWaiterDelay(5*time.Second))) + if waiterError != nil { + if !ignoreSigningJobFailure || !tfawserr.ErrCodeEquals(waiterError, request.WaiterResourceNotReadyErrorCode) { + return waiterError + } + } + + d.SetId(jobId) + + return resourceAwsSignerSigningJobRead(d, meta) +} + +func resourceAwsSignerSigningJobRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).signerconn + jobId := d.Id() + + describeSigningJobOutput, err := conn.DescribeSigningJob(&signer.DescribeSigningJobInput{ + JobId: aws.String(jobId), + }) + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, signer.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Signer Signing Job (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading Signer signing job (%s): %s", d.Id(), err) + } + + if err := d.Set("job_id", describeSigningJobOutput.JobId); err != nil { + return fmt.Errorf("error setting signer signing job id: %s", err) + } + + if err := d.Set("completed_at", aws.TimeValue(describeSigningJobOutput.CompletedAt).Format(time.RFC3339)); err != nil { + return fmt.Errorf("error setting signer signing job completed at: %s", err) + } + + if err := d.Set("created_at", aws.TimeValue(describeSigningJobOutput.CreatedAt).Format(time.RFC3339)); err != nil { + return fmt.Errorf("error setting signer signing job created at: %s", err) + } + + if err := d.Set("job_invoker", describeSigningJobOutput.JobInvoker); err != nil { + return fmt.Errorf("error setting signer signing job invoker: %s", err) + } + + if err := d.Set("job_owner", describeSigningJobOutput.JobOwner); err != nil { + return fmt.Errorf("error setting signer signing job owner: %s", err) + } + + if err := d.Set("platform_display_name", describeSigningJobOutput.PlatformDisplayName); err != nil { + return fmt.Errorf("error setting signer signing job platform display name: %s", err) + } + + if err := d.Set("platform_id", describeSigningJobOutput.PlatformId); err != nil { + return fmt.Errorf("error setting signer signing job platform id: %s", err) + } + + if err := d.Set("profile_name", describeSigningJobOutput.ProfileName); err != nil { + return fmt.Errorf("error setting signer signing job profile name: %s", err) + } + + if err := d.Set("profile_version", describeSigningJobOutput.ProfileVersion); err != nil { + return fmt.Errorf("error setting signer signing job profile version: %s", err) + } + + if err := d.Set("requested_by", describeSigningJobOutput.RequestedBy); err != nil { + return fmt.Errorf("error setting signer signing job requested by: %s", err) + } + + if err := d.Set("revocation_record", flattenSignerSigningJobRevocationRecord(describeSigningJobOutput.RevocationRecord)); err != nil { + return fmt.Errorf("error setting signer signing job revocation record: %s", err) + } + + signatureExpiresAt := "" + if describeSigningJobOutput.SignatureExpiresAt != nil { + signatureExpiresAt = aws.TimeValue(describeSigningJobOutput.SignatureExpiresAt).Format(time.RFC3339) + } + if err := d.Set("signature_expires_at", signatureExpiresAt); err != nil { + return fmt.Errorf("error setting signer signing job requested by: %s", err) + } + + if err := d.Set("signed_object", flattenSignerSigningJobSignedObject(describeSigningJobOutput.SignedObject)); err != nil { + return fmt.Errorf("error setting signer signing job signed object: %s", err) + } + + if err := d.Set("source", flattenSignerSigningJobSource(describeSigningJobOutput.Source)); err != nil { + return fmt.Errorf("error setting signer signing job source: %s", err) + } + + if err := d.Set("status", describeSigningJobOutput.Status); err != nil { + return fmt.Errorf("error setting signer signing job status: %s", err) + } + + if err := d.Set("status_reason", describeSigningJobOutput.StatusReason); err != nil { + return fmt.Errorf("error setting signer signing job status reason: %s", err) + } + + return nil +} + +func flattenSignerSigningJobRevocationRecord(apiObject *signer.SigningJobRevocationRecord) []interface{} { + if apiObject == nil { + return []interface{}{} + } + + tfMap := map[string]interface{}{} + + if v := apiObject.Reason; v != nil { + tfMap["reason"] = aws.StringValue(v) + } + + if v := apiObject.RevokedAt; v != nil { + tfMap["revoked_at"] = aws.TimeValue(v).Format(time.RFC3339) + } + + if v := apiObject.RevokedBy; v != nil { + tfMap["revoked_by"] = aws.StringValue(v) + } + + return []interface{}{tfMap} +} + +func flattenSignerSigningJobSource(apiObject *signer.Source) []interface{} { + if apiObject == nil || apiObject.S3 == nil { + return []interface{}{} + } + + tfMap := map[string]interface{}{ + "s3": flattenSignerSigningJobS3Source(apiObject.S3), + } + + return []interface{}{tfMap} +} + +func flattenSignerSigningJobS3Source(apiObject *signer.S3Source) []interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.BucketName; v != nil { + tfMap["bucket"] = aws.StringValue(v) + } + + if v := apiObject.Key; v != nil { + tfMap["key"] = aws.StringValue(v) + } + + if v := apiObject.Version; v != nil { + tfMap["version"] = aws.StringValue(v) + } + + return []interface{}{tfMap} +} + +func expandSignerSigningJobSource(tfList []interface{}) *signer.Source { + if tfList == nil || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + var source *signer.Source + if v, ok := tfMap["s3"].([]interface{}); ok && len(v) > 0 { + source = &signer.Source{ + S3: expandSignerSigningJobS3Source(v), + } + } + + return source +} + +func expandSignerSigningJobS3Source(tfList []interface{}) *signer.S3Source { + if tfList == nil || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + s3Source := &signer.S3Source{} + + if v, ok := tfMap["bucket"].(string); ok { + s3Source.BucketName = aws.String(v) + } + + if v, ok := tfMap["key"].(string); ok { + s3Source.Key = aws.String(v) + } + + if v, ok := tfMap["version"].(string); ok { + s3Source.Version = aws.String(v) + } + + return s3Source +} + +func expandSignerSigningJobDestination(tfList []interface{}) *signer.Destination { + if tfList == nil || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + var destination *signer.Destination + if v, ok := tfMap["s3"].([]interface{}); ok && len(v) > 0 { + destination = &signer.Destination{ + S3: expandSignerSigningJobS3Destination(v), + } + } + + return destination +} + +func expandSignerSigningJobS3Destination(tfList []interface{}) *signer.S3Destination { + if tfList == nil { + return nil + } + + tfMap := tfList[0].(map[string]interface{}) + s3Destination := &signer.S3Destination{} + + if _, ok := tfMap["bucket"]; ok { + s3Destination.BucketName = aws.String(tfMap["bucket"].(string)) + } + + if _, ok := tfMap["prefix"]; ok { + s3Destination.Prefix = aws.String(tfMap["prefix"].(string)) + } + + return s3Destination +} + +func flattenSignerSigningJobSignedObject(apiObject *signer.SignedObject) []interface{} { + if apiObject == nil || apiObject.S3 == nil { + return []interface{}{} + } + + tfMap := map[string]interface{}{ + "s3": flattenSignerSigningJobS3SignedObject(apiObject.S3), + } + + return []interface{}{tfMap} +} + +func flattenSignerSigningJobS3SignedObject(apiObject *signer.S3SignedObject) []interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.BucketName; v != nil { + tfMap["bucket"] = aws.StringValue(v) + } + + if v := apiObject.Key; v != nil { + tfMap["key"] = aws.StringValue(v) + } + + return []interface{}{tfMap} +} diff --git a/aws/resource_aws_signer_signing_job_test.go b/aws/resource_aws_signer_signing_job_test.go new file mode 100644 index 000000000000..aa6c0d59ca24 --- /dev/null +++ b/aws/resource_aws_signer_signing_job_test.go @@ -0,0 +1,118 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/signer" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccAWSSignerSigningJob_basic(t *testing.T) { + resourceName := "aws_signer_signing_job.test_job" + profileResourceName := "aws_signer_signing_profile.test_sp" + rString := acctest.RandString(48) + profileName := fmt.Sprintf("tf_acc_sp_basic_%s", rString) + + var job signer.DescribeSigningJobOutput + var conf signer.GetSigningProfileOutput + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccAWSSignerSigningJobConfig(profileName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSignerSigningProfileExists(profileResourceName, profileName, &conf), + testAccCheckAWSSignerSigningJobExists(resourceName, &job), + resource.TestCheckResourceAttr(resourceName, "platform_id", "AWSLambda-SHA384-ECDSA"), + resource.TestCheckResourceAttr(resourceName, "platform_display_name", "AWS Lambda"), + resource.TestCheckResourceAttr(resourceName, "status", "Succeeded"), + ), + }, + }, + }) + +} + +func testAccAWSSignerSigningJobConfig(profileName string) string { + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} + +resource "aws_signer_signing_profile" "test_sp" { + platform_id = "AWSLambda-SHA384-ECDSA" + name = "%s" +} + +resource "aws_s3_bucket" "bucket" { + bucket = "tf-signer-signing-bucket" + + versioning { + enabled = true + } + + force_destroy = true +} + +resource "aws_s3_bucket" "dest_bucket" { + bucket = "tf-signer-signing-dest-bucket" + force_destroy = true +} + +resource "aws_s3_bucket_object" "lambda_signing_code" { + bucket = aws_s3_bucket.bucket.bucket + key = "lambdatest.zip" + source = "test-fixtures/lambdatest.zip" +} + +resource "aws_signer_signing_job" "test_job" { + profile_name = aws_signer_signing_profile.test_sp.name + + source { + s3 { + bucket = aws_s3_bucket.bucket.bucket + key = aws_s3_bucket_object.lambda_signing_code.key + version = aws_s3_bucket_object.lambda_signing_code.version_id + } + } + + destination { + s3 { + bucket = aws_s3_bucket.dest_bucket.bucket + } + } +}`, profileName) +} + +func testAccCheckAWSSignerSigningJobExists(res string, job *signer.DescribeSigningJobOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[res] + if !ok { + return fmt.Errorf("Signing job not found: %s", res) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Signing job with that ID does not exist") + } + + conn := testAccProvider.Meta().(*AWSClient).signerconn + + params := &signer.DescribeSigningJobInput{ + JobId: aws.String(rs.Primary.ID), + } + + getJob, err := conn.DescribeSigningJob(params) + if err != nil { + return err + } + + *job = *getJob + + return nil + } +} diff --git a/aws/resource_aws_signer_signing_profile.go b/aws/resource_aws_signer_signing_profile.go new file mode 100644 index 000000000000..0b6c4fe042c8 --- /dev/null +++ b/aws/resource_aws_signer_signing_profile.go @@ -0,0 +1,275 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/signer" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" +) + +func resourceAwsSignerSigningProfile() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsSignerSigningProfileCreate, + Read: resourceAwsSignerSigningProfileRead, + Update: resourceAwsSignerSigningProfileUpdate, + Delete: resourceAwsSignerSigningProfileDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "platform_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + "AWSLambda-SHA384-ECDSA"}, + false), + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"name_prefix"}, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9_]{0,64}$`), "must be alphanumeric with max length of 64 characters"), + }, + "name_prefix": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"name"}, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9_]{0,38}$`), "must be alphanumeric with max length of 38 characters"), + }, + "signature_validity_period": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(signer.ValidityType_Values(), false), + }, + }, + }, + }, + "tags": tagsSchema(), + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "platform_display_name": { + Type: schema.TypeString, + Computed: true, + }, + "revocation_record": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "revocation_effective_from": { + Type: schema.TypeString, + Computed: true, + }, + "revoked_at": { + Type: schema.TypeString, + Computed: true, + }, + "revoked_by": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "version": { + Type: schema.TypeString, + Computed: true, + }, + "version_arn": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsSignerSigningProfileCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).signerconn + + log.Printf("[DEBUG] Creating Signer signing profile") + + profileName := naming.Generate(d.Get("name").(string), d.Get("name_prefix").(string)) + profileName = strings.Replace(profileName, "-", "_", -1) + + signingProfileInput := &signer.PutSigningProfileInput{ + ProfileName: aws.String(profileName), + PlatformId: aws.String(d.Get("platform_id").(string)), + } + + if v, exists := d.GetOk("signature_validity_period"); exists { + signatureValidityPeriod := v.([]interface{})[0].(map[string]interface{}) + signingProfileInput.SignatureValidityPeriod = &signer.SignatureValidityPeriod{ + Value: aws.Int64(int64(signatureValidityPeriod["value"].(int))), + Type: aws.String(signatureValidityPeriod["type"].(string)), + } + } + + if v, exists := d.GetOk("tags"); exists { + signingProfileInput.Tags = keyvaluetags.New(v.(map[string]interface{})).IgnoreAws().SignerTags() + } + + _, err := conn.PutSigningProfile(signingProfileInput) + if err != nil { + return fmt.Errorf("error creating Signer signing profile: %s", err) + } + + d.SetId(profileName) + + return resourceAwsSignerSigningProfileRead(d, meta) +} + +func resourceAwsSignerSigningProfileRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).signerconn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + signingProfileOutput, err := conn.GetSigningProfile(&signer.GetSigningProfileInput{ + ProfileName: aws.String(d.Id()), + }) + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, signer.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Signer Signing Profile (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading Signer signing profile (%s): %s", d.Id(), err) + } + + if err := d.Set("platform_id", signingProfileOutput.PlatformId); err != nil { + return fmt.Errorf("error setting signer signing profile platform id: %s", err) + } + + if err := d.Set("signature_validity_period", []interface{}{ + map[string]interface{}{ + "value": signingProfileOutput.SignatureValidityPeriod.Value, + "type": signingProfileOutput.SignatureValidityPeriod.Type, + }, + }); err != nil { + return fmt.Errorf("error setting signer signing profile signature validity period: %s", err) + } + + if err := d.Set("platform_display_name", signingProfileOutput.PlatformDisplayName); err != nil { + return fmt.Errorf("error setting signer signing profile platform display name: %s", err) + } + + if err := d.Set("name", signingProfileOutput.ProfileName); err != nil { + return fmt.Errorf("error setting signer signing profile name: %s", err) + } + + if err := d.Set("arn", signingProfileOutput.Arn); err != nil { + return fmt.Errorf("error setting signer signing profile arn: %s", err) + } + + if err := d.Set("version", signingProfileOutput.ProfileVersion); err != nil { + return fmt.Errorf("error setting signer signing profile version: %s", err) + } + + if err := d.Set("version_arn", signingProfileOutput.ProfileVersionArn); err != nil { + return fmt.Errorf("error setting signer signing profile version arn: %s", err) + } + + if err := d.Set("status", signingProfileOutput.Status); err != nil { + return fmt.Errorf("error setting signer signing profile status: %s", err) + } + + if err := d.Set("tags", keyvaluetags.SignerKeyValueTags(signingProfileOutput.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting signer signing profile tags: %s", err) + } + + if err := d.Set("revocation_record", flattenSignerSigningProfileRevocationRecord(signingProfileOutput.RevocationRecord)); err != nil { + return fmt.Errorf("error setting signer signing profile revocation record: %s", err) + } + + return nil +} + +func resourceAwsSignerSigningProfileUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).signerconn + + arn := d.Get("arn").(string) + if d.HasChange("tags") { + o, n := d.GetChange("tags") + + if err := keyvaluetags.SignerUpdateTags(conn, arn, o, n); err != nil { + return fmt.Errorf("error updating Signer signing profile (%s) tags: %s", arn, err) + } + } + + return resourceAwsSignerSigningProfileRead(d, meta) +} + +func resourceAwsSignerSigningProfileDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).signerconn + + _, err := conn.CancelSigningProfile(&signer.CancelSigningProfileInput{ + ProfileName: aws.String(d.Id()), + }) + + if err != nil { + if tfawserr.ErrCodeEquals(err, signer.ErrCodeResourceNotFoundException) { + return nil + } + return fmt.Errorf("error canceling Signer signing profile (%s): %s", d.Id(), err) + } + + log.Printf("[DEBUG] Signer signing profile %q canceled", d.Id()) + return nil +} + +func flattenSignerSigningProfileRevocationRecord(apiObject *signer.SigningProfileRevocationRecord) interface{} { + if apiObject == nil { + return []interface{}{} + } + + tfMap := map[string]interface{}{} + + if v := apiObject.RevocationEffectiveFrom; v != nil { + tfMap["revocation_effective_from"] = aws.TimeValue(v).Format(time.RFC3339) + } + + if v := apiObject.RevokedAt; v != nil { + tfMap["revoked_at"] = aws.TimeValue(v).Format(time.RFC3339) + } + + if v := apiObject.RevokedBy; v != nil { + tfMap["revoked_by"] = aws.StringValue(v) + } + + return []interface{}{tfMap} +} diff --git a/aws/resource_aws_signer_signing_profile_permission.go b/aws/resource_aws_signer_signing_profile_permission.go new file mode 100644 index 000000000000..f3d40c8bf801 --- /dev/null +++ b/aws/resource_aws_signer_signing_profile_permission.go @@ -0,0 +1,326 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/signer" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" + iamwaiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter" +) + +func resourceAwsSignerSigningProfilePermission() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsSignerSigningProfilePermissionCreate, + Read: resourceAwsSignerSigningProfilePermissionRead, + Delete: resourceAwsSignerSigningProfilePermissionDelete, + + Importer: &schema.ResourceImporter{ + State: resourceAwsSignerSigningProfilePermissionImport, + }, + + Schema: map[string]*schema.Schema{ + "profile_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(2, 64), + }, + "action": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + "signer:StartSigningJob", + "signer:GetSigningProfile", + "signer:RevokeSignature"}, + false), + }, + "principal": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "profile_version": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(10, 10), + }, + "statement_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"statement_id_prefix"}, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9-_]{0,64}$`), "must be alphanumeric with max length of 64 characters"), + }, + "statement_id_prefix": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"statement_id"}, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9-_]{0,38}$`), "must be alphanumeric with max length of 38 characters"), + }, + }, + } +} + +func resourceAwsSignerSigningProfilePermissionCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).signerconn + + profileName := d.Get("profile_name").(string) + + awsMutexKV.Lock(profileName) + defer awsMutexKV.Unlock(profileName) + + listProfilePermissionsInput := &signer.ListProfilePermissionsInput{ + ProfileName: aws.String(profileName), + } + + var revisionId string + getProfilePermissionsOutput, err := conn.ListProfilePermissions(listProfilePermissionsInput) + if err != nil { + if isAWSErr(err, signer.ErrCodeResourceNotFoundException, "") { + revisionId = "" + } else { + return err + } + } else { + revisionId = *getProfilePermissionsOutput.RevisionId + } + + statementId := naming.Generate(d.Get("statement_id").(string), d.Get("statement_id_prefix").(string)) + + addProfilePermissionInput := &signer.AddProfilePermissionInput{ + Action: aws.String(d.Get("action").(string)), + Principal: aws.String(d.Get("principal").(string)), + ProfileName: aws.String(profileName), + RevisionId: aws.String(revisionId), + StatementId: aws.String(statementId), + } + + if v, ok := d.GetOk("profile_version"); ok { + addProfilePermissionInput.ProfileVersion = aws.String(v.(string)) + } + + log.Printf("[DEBUG] Adding new Signer signing profile permission: %s", addProfilePermissionInput) + //var addProfilePermissionOutput *signer.AddProfilePermissionOutput + // Retry for IAM eventual consistency + err = resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError { + var err error + _, err = conn.AddProfilePermission(addProfilePermissionInput) + + if isAWSErr(err, signer.ErrCodeConflictException, "") || isAWSErr(err, signer.ErrCodeResourceNotFoundException, "") { + return resource.RetryableError(err) + } + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + if isResourceTimeoutError(err) { + _, err = conn.AddProfilePermission(addProfilePermissionInput) + } + + if err != nil { + return fmt.Errorf("error adding new Signer signing profile permission for %q: %s", profileName, err) + } + + err = resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError { + // IAM is eventually consistent :/ + err := resourceAwsSignerSigningProfilePermissionRead(d, meta) + if err != nil { + if isAWSErr(err, signer.ErrCodeResourceNotFoundException, "") { + return resource.RetryableError( + fmt.Errorf("error reading newly created Signer signing profile permission for %s, retrying: %s", + *addProfilePermissionInput.ProfileName, err)) + } + + log.Printf("[ERROR] An actual error occurred when expecting Signer signing profile permission to be there: %s", err) + return resource.NonRetryableError(err) + } + return nil + }) + if isResourceTimeoutError(err) { + err = resourceAwsSignerSigningProfilePermissionRead(d, meta) + } + if err != nil { + return fmt.Errorf("error reading new Signer permissions: %s", err) + } + + d.Set("statement_id", statementId) + d.SetId(fmt.Sprintf("%s/%s", profileName, statementId)) + + return nil +} + +func resourceAwsSignerSigningProfilePermissionRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).signerconn + + listProfilePermissionsInput := &signer.ListProfilePermissionsInput{ + ProfileName: aws.String(d.Get("profile_name").(string)), + } + + log.Printf("[DEBUG] Getting Signer signing profile permissions: %s", listProfilePermissionsInput) + var listProfilePermissionsOutput *signer.ListProfilePermissionsOutput + err := resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError { + // IAM is eventually consistent :/ + var err error + listProfilePermissionsOutput, err = conn.ListProfilePermissions(listProfilePermissionsInput) + if err != nil { + if isAWSErr(err, signer.ErrCodeResourceNotFoundException, "") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + + return nil + }) + if isResourceTimeoutError(err) { + listProfilePermissionsOutput, err = conn.ListProfilePermissions(listProfilePermissionsInput) + } + + if !d.IsNewResource() && isAWSErr(err, signer.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] No Signer Signing Profile Permissions found (%s), removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading Signer Signing Profile Permissions (%s): %w", d.Id(), err) + } + + statementId := d.Get("statement_id").(string) + permission := getProfilePermission(listProfilePermissionsOutput.Permissions, statementId) + if permission == nil { + log.Printf("[WARN] No Signer signing profile permission found matching statement id: %s", statementId) + d.SetId("") + return nil + } + + if err := d.Set("action", permission.Action); err != nil { + return fmt.Errorf("error setting signer signing profile permission action: %s", err) + } + if err := d.Set("principal", permission.Principal); err != nil { + return fmt.Errorf("error setting signer signing profile permission principal: %s", err) + } + if err := d.Set("profile_version", permission.ProfileVersion); err != nil { + return fmt.Errorf("error setting signer signing profile permission profile version: %s", err) + } + if err := d.Set("statement_id", permission.StatementId); err != nil { + return fmt.Errorf("error setting signer signing profile permission statement id: %s", err) + } + + return nil +} + +func getProfilePermission(permissions []*signer.Permission, statementId string) *signer.Permission { + for _, permission := range permissions { + if permission == nil { + continue + } + + if aws.StringValue(permission.StatementId) == statementId { + return permission + } + } + + return nil +} + +func resourceAwsSignerSigningProfilePermissionDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).signerconn + + profileName := d.Get("profile_name").(string) + + awsMutexKV.Lock(profileName) + defer awsMutexKV.Unlock(profileName) + + listProfilePermissionsInput := &signer.ListProfilePermissionsInput{ + ProfileName: aws.String(profileName), + } + + listProfilePermissionsOutput, err := conn.ListProfilePermissions(listProfilePermissionsInput) + if err != nil { + if isAWSErr(err, signer.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] No Signer signing profile permission found for: %v", listProfilePermissionsInput) + return nil + } + return err + } + + revisionId := aws.StringValue(listProfilePermissionsOutput.RevisionId) + + statementId := d.Get("statement_id").(string) + permission := getProfilePermission(listProfilePermissionsOutput.Permissions, statementId) + if permission == nil { + log.Printf("[WARN] No Signer signing profile permission found matching statement id: %s", statementId) + return nil + } + + removeProfilePermissionInput := &signer.RemoveProfilePermissionInput{ + ProfileName: aws.String(profileName), + RevisionId: aws.String(revisionId), + StatementId: permission.StatementId, + } + + log.Printf("[DEBUG] Removing Signer singing profile permission: %s", removeProfilePermissionInput) + _, err = conn.RemoveProfilePermission(removeProfilePermissionInput) + if err != nil { + if tfawserr.ErrCodeEquals(err, signer.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] No Signer Signing Profile Permission found: %v", removeProfilePermissionInput) + return nil + } + return fmt.Errorf("error removing Signer Signing Profile Permission (%s): %w", d.Id(), err) + } + + params := &signer.ListProfilePermissionsInput{ + ProfileName: aws.String(profileName), + } + + resp, err := conn.ListProfilePermissions(params) + + if isAWSErr(err, signer.ErrCodeResourceNotFoundException, "") { + return nil + } + + if err != nil { + return fmt.Errorf("error getting Signer signing profile permissions: %s", err) + } + + if len(resp.Permissions) > 0 { + permission := getProfilePermission(resp.Permissions, statementId) + if permission != nil { + return fmt.Errorf("failed to delete Signer singing profile permission with ID %q", statementId) + } + } + + log.Printf("[DEBUG] Signer signing profile permission with ID %q removed", statementId) + + return nil +} + +func resourceAwsSignerSigningProfilePermissionImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + idParts := strings.Split(d.Id(), "/") + if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { + return nil, fmt.Errorf("unexpected format of ID (%q), expected PROFILE_NAME/STATEMENT_ID", d.Id()) + } + + profileName := idParts[0] + statementId := idParts[1] + + d.Set("profile_name", profileName) + d.Set("statement_id", statementId) + d.SetId(statementId) + return []*schema.ResourceData{d}, nil +} diff --git a/aws/resource_aws_signer_signing_profile_permission_test.go b/aws/resource_aws_signer_signing_profile_permission_test.go new file mode 100644 index 000000000000..b96255dbe225 --- /dev/null +++ b/aws/resource_aws_signer_signing_profile_permission_test.go @@ -0,0 +1,251 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/signer" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" +) + +func TestAccAWSSignerSigningProfilePermission_basic(t *testing.T) { + resourceName := "aws_signer_signing_profile_permission.test_sp_permission" + profileResourceName := "aws_signer_signing_profile.test_sp" + rString := acctest.RandString(53) + profileName := fmt.Sprintf("tf_acc_spp_%s", rString) + + var conf signer.GetSigningProfileOutput + var sppconf signer.ListProfilePermissionsOutput + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSignerSigningProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSignerSigningProfilePermissionConfig(profileName), + Destroy: false, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSignerSigningProfileExists(profileResourceName, profileName, &conf), + testAccCheckAWSSignerSigningProfilePermissionExists(resourceName, profileName, &sppconf), + naming.TestCheckResourceAttrNameGenerated(resourceName, "statement_id"), + ), + }, + { + ResourceName: profileResourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name_prefix"}, + }, + }, + }) +} + +func TestAccAWSSignerSigningProfilePermission_GetSigningProfile(t *testing.T) { + resourceName := "aws_signer_signing_profile_permission.test_sp_permission" + profileResourceName := "aws_signer_signing_profile.test_sp" + rString := acctest.RandString(53) + profileName := fmt.Sprintf("tf_acc_spp_%s", rString) + + var conf signer.GetSigningProfileOutput + var sppconf signer.ListProfilePermissionsOutput + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSignerSigningProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSignerSigningProfilePermissionGetSP(profileName), + Destroy: false, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSignerSigningProfileExists(profileResourceName, profileName, &conf), + testAccCheckAWSSignerSigningProfilePermissionExists(resourceName, profileName, &sppconf), + ), + }, + { + ResourceName: profileResourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name_prefix"}, + }, + { + Config: testAccAWSSignerSigningProfilePermissionRevokeSignature(profileName), + Destroy: false, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSignerSigningProfileExists(profileResourceName, profileName, &conf), + testAccCheckAWSSignerSigningProfilePermissionExists(resourceName, profileName, &sppconf), + ), + }, + }, + }) +} + +func TestAccAWSSignerSigningProfilePermission_StartSigningJob_GetSP(t *testing.T) { + resourceName1 := "aws_signer_signing_profile_permission.sp1_perm" + resourceName2 := "aws_signer_signing_profile_permission.sp2_perm" + profileResourceName := "aws_signer_signing_profile.test_sp" + rString := acctest.RandString(53) + profileName := fmt.Sprintf("tf_acc_spp_%s", rString) + + var conf signer.GetSigningProfileOutput + var sppconf signer.ListProfilePermissionsOutput + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSignerSigningProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSignerSigningProfilePermissionStartSigningJobGetSP(profileName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSignerSigningProfileExists(profileResourceName, profileName, &conf), + testAccCheckAWSSignerSigningProfilePermissionExists(resourceName1, profileName, &sppconf), + testAccCheckAWSSignerSigningProfilePermissionExists(resourceName2, profileName, &sppconf), + ), + }, + { + ResourceName: profileResourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name_prefix"}, + }, + }, + }) +} + +func TestAccAWSSignerSigningProfilePermission_StatementPrefix(t *testing.T) { + resourceName := "aws_signer_signing_profile_permission.sp1_perm" + profileResourceName := "aws_signer_signing_profile.test_sp" + rString := acctest.RandString(53) + profileName := fmt.Sprintf("tf_acc_spp_%s", rString) + statementNamePrefix := fmt.Sprintf("tf_acc_spp_statement_") + + //var conf signer.GetSigningProfileOutput + var sppconf signer.ListProfilePermissionsOutput + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSignerSigningProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSignerSigningProfilePermissionStatementPrefix(statementNamePrefix, profileName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSignerSigningProfilePermissionExists(resourceName, profileName, &sppconf), + naming.TestCheckResourceAttrNameFromPrefix(resourceName, "statement_id", statementNamePrefix), + ), + }, + { + ResourceName: profileResourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name_prefix"}, + }, + }, + }) +} + +func testAccAWSSignerSigningProfilePermissionConfig(profileName string) string { + return fmt.Sprintf(testAccAWSSignerSigningProfilePermissionConfig_base(profileName) + ` +data "aws_caller_identity" "current" {} + +resource "aws_signer_signing_profile_permission" "test_sp_permission" { + profile_name = aws_signer_signing_profile.test_sp.name + action = "signer:StartSigningJob" + principal = data.aws_caller_identity.current.account_id +}`) +} + +func testAccAWSSignerSigningProfilePermissionStartSigningJobGetSP(profileName string) string { + return fmt.Sprintf(testAccAWSSignerSigningProfilePermissionConfig_base(profileName) + ` +data "aws_caller_identity" "current" {} + +resource "aws_signer_signing_profile_permission" "sp1_perm" { + profile_name = aws_signer_signing_profile.test_sp.name + action = "signer:StartSigningJob" + principal = data.aws_caller_identity.current.account_id + statement_id = "statementid1" +} + +resource "aws_signer_signing_profile_permission" "sp2_perm" { + profile_name = aws_signer_signing_profile.test_sp.name + action = "signer:GetSigningProfile" + principal = data.aws_caller_identity.current.account_id + statement_id = "statementid2" +}`) +} + +func testAccAWSSignerSigningProfilePermissionStatementPrefix(statementNamePrefix, profileName string) string { + return fmt.Sprintf(testAccAWSSignerSigningProfilePermissionConfig_base(profileName)+` +data "aws_caller_identity" "current" {} + +resource "aws_signer_signing_profile_permission" "sp1_perm" { + profile_name = aws_signer_signing_profile.test_sp.name + action = "signer:StartSigningJob" + principal = data.aws_caller_identity.current.account_id + statement_id_prefix = %[1]q +}`, statementNamePrefix) +} + +func testAccAWSSignerSigningProfilePermissionGetSP(profileName string) string { + return fmt.Sprintf(testAccAWSSignerSigningProfilePermissionConfig_base(profileName) + ` +data "aws_caller_identity" "current" {} + +resource "aws_signer_signing_profile_permission" "test_sp_permission" { + profile_name = aws_signer_signing_profile.test_sp.name + action = "signer:GetSigningProfile" + principal = data.aws_caller_identity.current.account_id +}`) +} + +func testAccAWSSignerSigningProfilePermissionRevokeSignature(profileName string) string { + return fmt.Sprintf(testAccAWSSignerSigningProfilePermissionConfig_base(profileName) + ` +data "aws_caller_identity" "current" {} + +resource "aws_signer_signing_profile_permission" "test_sp_permission" { + profile_name = aws_signer_signing_profile.test_sp.name + action = "signer:RevokeSignature" + principal = data.aws_caller_identity.current.account_id +}`) +} + +func testAccAWSSignerSigningProfilePermissionConfig_base(profileName string) string { + return fmt.Sprintf(` +resource "aws_signer_signing_profile" "test_sp" { + platform_id = "AWSLambda-SHA384-ECDSA" + name = "%s" +}`, profileName) +} + +func testAccCheckAWSSignerSigningProfilePermissionExists(res, profileName string, spp *signer.ListProfilePermissionsOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[res] + if !ok { + return fmt.Errorf("Signing profile permission not found: %s", res) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Signing Profile with that ARN does not exist") + } + + conn := testAccProvider.Meta().(*AWSClient).signerconn + + params := &signer.ListProfilePermissionsInput{ + ProfileName: aws.String(profileName), + } + + getSp, err := conn.ListProfilePermissions(params) + if err != nil { + return err + } + + *spp = *getSp + + return nil + } +} diff --git a/aws/resource_aws_signer_signing_profile_test.go b/aws/resource_aws_signer_signing_profile_test.go new file mode 100644 index 000000000000..93ae7268fed0 --- /dev/null +++ b/aws/resource_aws_signer_signing_profile_test.go @@ -0,0 +1,306 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/signer" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccAWSSignerSigningProfile_basic(t *testing.T) { + resourceName := "aws_signer_signing_profile.test_sp" + rString := acctest.RandString(48) + profileName := fmt.Sprintf("tf_acc_sp_basic_%s", rString) + + var conf signer.GetSigningProfileOutput + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSignerSigningProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSignerSigningProfileConfigProvidedName(profileName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSignerSigningProfileExists(resourceName, profileName, &conf), + resource.TestMatchResourceAttr(resourceName, "name", + regexp.MustCompile("^[a-zA-Z0-9_]{0,64}$")), + resource.TestCheckResourceAttr(resourceName, "platform_id", "AWSLambda-SHA384-ECDSA"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name_prefix"}, + }, + }, + }) +} + +func TestAccAWSSignerSigningProfile_GenerateNameWithNamePrefix(t *testing.T) { + resourceName := "aws_signer_signing_profile.test_sp" + namePrefix := fmt.Sprintf("tf_acc_sp_basic_") + + var conf signer.GetSigningProfileOutput + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSignerSigningProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSignerSigningProfileConfig(namePrefix), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSignerSigningProfileGeneratedNameExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "platform_id", "AWSLambda-SHA384-ECDSA"), + ), + }, + }, + }) +} + +func TestAccAWSSignerSigningProfile_GenerateName(t *testing.T) { + resourceName := "aws_signer_signing_profile.test_sp" + + var conf signer.GetSigningProfileOutput + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSignerSigningProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSignerSigningProfileConfigGenerateName(), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSignerSigningProfileGeneratedNameExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "platform_id", "AWSLambda-SHA384-ECDSA"), + ), + }, + }, + }) +} + +func TestAccAWSSignerSigningProfile_tags(t *testing.T) { + resourceName := "aws_signer_signing_profile.test_sp" + namePrefix := fmt.Sprintf("tf_acc_sp_basic_") + + var conf signer.GetSigningProfileOutput + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSignerSigningProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSignerSigningProfileConfigTags(namePrefix), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSignerSigningProfileGeneratedNameExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "tags.tag1", "value1"), + resource.TestCheckResourceAttr(resourceName, "tags.tag2", "value2"), + ), + }, + { + Config: testAccAWSSignerSigningProfileUpdateTags(), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSignerSigningProfileGeneratedNameExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "tags.tag1", "prod"), + ), + }, + }, + }) +} + +func TestAccAWSSignerSigningProfile_SignatureValidityPeriod(t *testing.T) { + resourceName := "aws_signer_signing_profile.test_sp" + namePrefix := fmt.Sprintf("tf_acc_sp_basic_") + + var conf signer.GetSigningProfileOutput + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSignerSigningProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSignerSigningProfileConfigSVP(namePrefix), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSignerSigningProfileGeneratedNameExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "signature_validity_period.0.type", "DAYS"), + resource.TestCheckResourceAttr(resourceName, "signature_validity_period.0.value", "10"), + ), + }, + { + Config: testAccAWSSignerSigningProfileUpdateSVP(), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSignerSigningProfileGeneratedNameExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "signature_validity_period.0.type", "MONTHS"), + resource.TestCheckResourceAttr(resourceName, "signature_validity_period.0.value", "10"), + ), + }, + }, + }) +} + +func testAccAWSSignerSigningProfileConfig(namePrefix string) string { + return baseAccAWSSignerSigningProfileConfig(namePrefix) +} + +func testAccAWSSignerSigningProfileConfigGenerateName() string { + return fmt.Sprintf(` +resource "aws_signer_signing_profile" "test_sp" { + platform_id = "AWSLambda-SHA384-ECDSA" +}`) +} + +func testAccAWSSignerSigningProfileConfigProvidedName(profileName string) string { + return fmt.Sprintf(` +resource "aws_signer_signing_profile" "test_sp" { + platform_id = "AWSLambda-SHA384-ECDSA" + name = "%s" +}`, profileName) +} + +func testAccAWSSignerSigningProfileConfigTags(namePrefix string) string { + return fmt.Sprintf(` +resource "aws_signer_signing_profile" "test_sp" { + platform_id = "AWSLambda-SHA384-ECDSA" + name_prefix = "%s" + tags = { + "tag1" = "value1" + "tag2" = "value2" + } +}`, namePrefix) +} + +func testAccAWSSignerSigningProfileConfigSVP(namePrefix string) string { + return fmt.Sprintf(` +resource "aws_signer_signing_profile" "test_sp" { + platform_id = "AWSLambda-SHA384-ECDSA" + name_prefix = "%s" + + signature_validity_period { + value = 10 + type = "DAYS" + } +} +`, namePrefix) +} + +func testAccAWSSignerSigningProfileUpdateSVP() string { + return fmt.Sprintf(` +resource "aws_signer_signing_profile" "test_sp" { + platform_id = "AWSLambda-SHA384-ECDSA" + + signature_validity_period { + value = 10 + type = "MONTHS" + } +} +`) +} + +func testAccAWSSignerSigningProfileUpdateTags() string { + return fmt.Sprintf(` +resource "aws_signer_signing_profile" "test_sp" { + platform_id = "AWSLambda-SHA384-ECDSA" + tags = { + "tag1" = "prod" + } +} +`) +} + +func baseAccAWSSignerSigningProfileConfig(namePrefix string) string { + return fmt.Sprintf(` +resource "aws_signer_signing_profile" "test_sp" { + platform_id = "AWSLambda-SHA384-ECDSA" + name_prefix = "%s" +} +`, namePrefix) +} + +func testAccCheckAWSSignerSigningProfileExists(res, profileName string, sp *signer.GetSigningProfileOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[res] + if !ok { + return fmt.Errorf("Signing profile not found: %s", res) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Signing Profile with that ARN does not exist") + } + + conn := testAccProvider.Meta().(*AWSClient).signerconn + + params := &signer.GetSigningProfileInput{ + ProfileName: aws.String(profileName), + } + + getSp, err := conn.GetSigningProfile(params) + if err != nil { + return err + } + + *sp = *getSp + + return nil + } +} + +func testAccCheckAWSSignerSigningProfileGeneratedNameExists(res string, sp *signer.GetSigningProfileOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[res] + if !ok { + return fmt.Errorf("Signing profile not found: %s", res) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Signing Profile with that ARN does not exist") + } + + conn := testAccProvider.Meta().(*AWSClient).signerconn + + params := &signer.GetSigningProfileInput{ + ProfileName: aws.String(rs.Primary.ID), + } + + getSp, err := conn.GetSigningProfile(params) + if err != nil { + return err + } + + *sp = *getSp + + return nil + } +} + +func testAccCheckAWSSignerSigningProfileDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).signerconn + + time.Sleep(5 * time.Second) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_signer_signing_profile" { + continue + } + + out, err := conn.GetSigningProfile(&signer.GetSigningProfileInput{ + ProfileName: aws.String(rs.Primary.ID), + }) + + if *out.Status != signer.SigningProfileStatusCanceled && err == nil { + return fmt.Errorf("Signing Profile not cancelled%s", *out.ProfileName) + } + + } + + return nil +} diff --git a/infrastructure/repository/labels-service.tf b/infrastructure/repository/labels-service.tf index 2b76e122171f..33aa7614aca5 100644 --- a/infrastructure/repository/labels-service.tf +++ b/infrastructure/repository/labels-service.tf @@ -173,6 +173,7 @@ variable "service_labels" { "sesv2", "sfn", "shield", + "signer", "simpledb", "sms", "snowball", diff --git a/website/allowed-subcategories.txt b/website/allowed-subcategories.txt index 88fa369eeeb5..a03a91258230 100644 --- a/website/allowed-subcategories.txt +++ b/website/allowed-subcategories.txt @@ -114,6 +114,7 @@ Service Discovery Service Quotas Shield SimpleDB +Signer Step Function (SFN) Storage Gateway Synthetics diff --git a/website/docs/d/signer_signing_job.html.markdown b/website/docs/d/signer_signing_job.html.markdown new file mode 100644 index 000000000000..f85077e07245 --- /dev/null +++ b/website/docs/d/signer_signing_job.html.markdown @@ -0,0 +1,45 @@ +--- +subcategory: "Signer" +layout: "aws" +page_title: "AWS: aws_signer_signing_job" +description: |- + Provides a Signer Signing Job data source. +--- + +# Data Source: aws_signer_signing_job + +Provides information about a Signer Signing Job. + +## Example Usage + +```hcl +data "aws_signer_signing_job" "build_signing_job" { + job_id = "9ed7e5c3-b8d4-4da0-8459-44e0b068f7ee" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `job_id` - (Required) The ID of the signing job on output. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `completed_at` - Date and time in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) that the signing job was completed. +* `created_at` - Date and time in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) that the signing job was created. +* `job_invoker` - The IAM entity that initiated the signing job. +* `job_owner` - The AWS account ID of the job owner. +* `platform_display_name` - A human-readable name for the signing platform associated with the signing job. +* `platform_id` - The platform to which your signed code image will be distributed. +* `profile_name` - The name of the profile that initiated the signing operation. +* `profile_version` - The version of the signing profile used to initiate the signing job. +* `requested_by` - The IAM principal that requested the signing job. +* `revocation_record` - A revocation record if the signature generated by the signing job has been revoked. Contains a timestamp and the ID of the IAM entity that revoked the signature. +* `signature_expires_at` - The time when the signature of a signing job expires. +* `signed_object` - Name of the S3 bucket where the signed code image is saved by code signing. +* `source` - The object that contains the name of your S3 bucket or your raw code. +* `status` - Status of the signing job. +* `status_reason` - String value that contains the status reason. diff --git a/website/docs/d/signer_signing_profile.html.markdown b/website/docs/d/signer_signing_profile.html.markdown new file mode 100644 index 000000000000..7f3787b3893d --- /dev/null +++ b/website/docs/d/signer_signing_profile.html.markdown @@ -0,0 +1,39 @@ +--- +subcategory: "Signer" +layout: "aws" +page_title: "AWS: aws_signer_signing_profile" +description: |- + Provides a Signer Signing Profile data source. +--- + +# Data Source: aws_signer_signing_profile + +Provides information about a Signer Signing Profile. + +## Example Usage + +```hcl +data "aws_signer_signing_profile" "production_signing_profile" { + name = "prod_profile_DdW3Mk1foYL88fajut4mTVFGpuwfd4ACO6ANL0D1uIj7lrn8adK" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the target signing profile. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `arn` - The Amazon Resource Name (ARN) for the signing profile. +* `platform_display_name` - A human-readable name for the signing platform associated with the signing profile. +* `platform_id` - The ID of the platform that is used by the target signing profile. +* `revocation_record` - Revocation information for a signing profile. +* `signature_validity_period` - The validity period for a signing job. +* `status` - The status of the target signing profile. +* `tags` - A list of tags associated with the signing profile. +* `version` - The current version of the signing profile. +* `version_arn` - The signing profile ARN, including the profile version. diff --git a/website/docs/guides/custom-service-endpoints.html.md b/website/docs/guides/custom-service-endpoints.html.md index ef3996b94e88..9c269c2a505b 100644 --- a/website/docs/guides/custom-service-endpoints.html.md +++ b/website/docs/guides/custom-service-endpoints.html.md @@ -182,6 +182,7 @@ The Terraform AWS Provider allows the following endpoints to be customized:
servicequotas
ses
shield
signer
sns
sqs
ssm