Repository: gruntwork-io/terragrunt (language: go)

Issue:
Improve S3 errors logging
**Describe the solution you'd like**
Improve S3 errors logging to include in which operation failed to make finding root cause easier.

**Describe alternatives you've considered**
Enabling debug logs helps but it also add more unrelated logs 

**Additional context**
N/A



Discussion / hints:

Fix patch:
diff --git a/remote/remote_state_gcs.go b/remote/remote_state_gcs.go
index 087a433097..6fe284c4af 100644
--- a/remote/remote_state_gcs.go
+++ b/remote/remote_state_gcs.go
@@ -385,7 +385,7 @@ func CreateGCSBucket(gcsClient *storage.Client, config *ExtendedRemoteStateConfi
 	}
 
 	err := bucket.Create(ctx, projectID, bucketAttrs)
-	return errors.WithStackTrace(err)
+	return errors.WithStackTraceAndPrefix(err, "Error creating GCS bucket %s", config.remoteStateConfigGCS.Bucket)
 }
 
 // GCP is eventually consistent, so after creating a GCS bucket, this method can be used to wait until the information
diff --git a/remote/remote_state_s3.go b/remote/remote_state_s3.go
index 978df6f218..2a29839deb 100644
--- a/remote/remote_state_s3.go
+++ b/remote/remote_state_s3.go
@@ -910,12 +910,12 @@ func EnableRootAccesstoS3Bucket(s3Client *s3.S3, config *ExtendedRemoteStateConf
 
 	accountID, err := aws_helper.GetAWSAccountID(config.GetAwsSessionConfig(), terragruntOptions)
 	if err != nil {
-		return errors.WithStackTrace(err)
+		return errors.WithStackTraceAndPrefix(err, "Error getting AWS account ID %s for bucket %s", accountID, bucket)
 	}
 
 	partition, err := aws_helper.GetAWSPartition(config.GetAwsSessionConfig(), terragruntOptions)
 	if err != nil {
-		return errors.WithStackTrace(err)
+		return errors.WithStackTraceAndPrefix(err, "Error getting AWS partition %s for bucket %s", partition, bucket)
 	}
 
 	var policyInBucket aws_helper.Policy
@@ -931,7 +931,7 @@ func EnableRootAccesstoS3Bucket(s3Client *s3.S3, config *ExtendedRemoteStateConf
 		terragruntOptions.Logger.Debugf("Policy already exists for bucket %s", bucket)
 		policyInBucket, err = aws_helper.UnmarshalPolicy(*policyOutput.Policy)
 		if err != nil {
-			return errors.WithStackTrace(err)
+			return errors.WithStackTraceAndPrefix(err, "Error unmarshalling policy for bucket %s", bucket)
 		}
 	}
 
@@ -966,7 +966,7 @@ func EnableRootAccesstoS3Bucket(s3Client *s3.S3, config *ExtendedRemoteStateConf
 	rootS3Policy.Statement = append(rootS3Policy.Statement, policyInBucket.Statement...)
 	policy, err := aws_helper.MarshalPolicy(rootS3Policy)
 	if err != nil {
-		return errors.WithStackTrace(err)
+		return errors.WithStackTraceAndPrefix(err, "Error marshalling policy for bucket %s", bucket)
 	}
 
 	_, err = s3Client.PutBucketPolicy(&s3.PutBucketPolicyInput{
@@ -974,7 +974,7 @@ func EnableRootAccesstoS3Bucket(s3Client *s3.S3, config *ExtendedRemoteStateConf
 		Policy: aws.String(string(policy)),
 	})
 	if err != nil {
-		return errors.WithStackTrace(err)
+		return errors.WithStackTraceAndPrefix(err, "Error putting policy for bucket %s", bucket)
 	}
 
 	terragruntOptions.Logger.Debugf("Enabled root access to bucket %s", bucket)
@@ -996,7 +996,7 @@ func checkIfBucketRootAccess(s3Client *s3.S3, config *RemoteStateConfigS3, terra
 			}
 		}
 		terragruntOptions.Logger.Debugf("Could not get policy for bucket %s", config.Bucket)
-		return false, errors.WithStackTrace(err)
+		return false, errors.WithStackTraceAndPrefix(err, "Error checking if bucket %s is have root access", config.Bucket)
 	}
 
 	// If the bucket has no policy, it is not enforced
@@ -1006,7 +1006,7 @@ func checkIfBucketRootAccess(s3Client *s3.S3, config *RemoteStateConfigS3, terra
 
 	policyInBucket, err := aws_helper.UnmarshalPolicy(*policyOutput.Policy)
 	if err != nil {
-		return false, errors.WithStackTrace(err)
+		return false, errors.WithStackTraceAndPrefix(err, "Error unmarshalling policy for bucket %s", config.Bucket)
 	}
 
 	for _, statement := range policyInBucket.Statement {
@@ -1042,7 +1042,7 @@ func EnableEnforcedTLSAccesstoS3Bucket(s3Client *s3.S3, bucket string, config *E
 		terragruntOptions.Logger.Debugf("Policy already exists for bucket %s", bucket)
 		policyInBucket, err = aws_helper.UnmarshalPolicy(*policyOutput.Policy)
 		if err != nil {
-			return errors.WithStackTrace(err)
+			return errors.WithStackTraceAndPrefix(err, "Error unmarshalling policy for bucket %s", bucket)
 		}
 	}
 
@@ -1078,7 +1078,7 @@ func EnableEnforcedTLSAccesstoS3Bucket(s3Client *s3.S3, bucket string, config *E
 	tlsS3Policy.Statement = append(tlsS3Policy.Statement, policyInBucket.Statement...)
 	policy, err := aws_helper.MarshalPolicy(tlsS3Policy)
 	if err != nil {
-		return errors.WithStackTrace(err)
+		return errors.WithStackTraceAndPrefix(err, "Error marshalling policy for bucket %s", bucket)
 	}
 
 	_, err = s3Client.PutBucketPolicy(&s3.PutBucketPolicyInput{
@@ -1086,7 +1086,7 @@ func EnableEnforcedTLSAccesstoS3Bucket(s3Client *s3.S3, bucket string, config *E
 		Policy: aws.String(string(policy)),
 	})
 	if err != nil {
-		return errors.WithStackTrace(err)
+		return errors.WithStackTraceAndPrefix(err, "Error putting policy for bucket %s", bucket)
 	}
 
 	terragruntOptions.Logger.Debugf("Enabled enforced TLS access for bucket %s", bucket)
@@ -1111,7 +1111,7 @@ func checkIfBucketEnforcedTLS(s3Client *s3.S3, config *RemoteStateConfigS3, terr
 			}
 		}
 
-		return false, errors.WithStackTrace(err)
+		return false, errors.WithStackTraceAndPrefix(err, "Error checking if bucket %s is enforced with TLS", config.Bucket)
 	}
 
 	if policyOutput.Policy == nil {
@@ -1120,7 +1120,7 @@ func checkIfBucketEnforcedTLS(s3Client *s3.S3, config *RemoteStateConfigS3, terr
 
 	policyInBucket, err := aws_helper.UnmarshalPolicy(*policyOutput.Policy)
 	if err != nil {
-		return false, errors.WithStackTrace(err)
+		return false, errors.WithStackTraceAndPrefix(err, "Error unmarshalling policy for bucket %s", config.Bucket)
 	}
 
 	for _, statement := range policyInBucket.Statement {
@@ -1144,7 +1144,7 @@ func EnableVersioningForS3Bucket(s3Client *s3.S3, config *RemoteStateConfigS3, t
 
 	_, err := s3Client.PutBucketVersioning(&input)
 	if err != nil {
-		return errors.WithStackTrace(err)
+		return errors.WithStackTraceAndPrefix(err, "Error enabling versioning on S3 bucket %s", config.Bucket)
 	}
 
 	terragruntOptions.Logger.Debugf("Enabled versioning on S3 bucket %s", config.Bucket)
@@ -1182,7 +1182,7 @@ func EnableSSEForS3BucketWide(s3Client *s3.S3, bucketName string, algorithm stri
 
 	_, err = s3Client.PutBucketEncryption(input)
 	if err != nil {
-		return errors.WithStackTrace(err)
+		return errors.WithStackTraceAndPrefix(err, "Error enabling bucket-wide SSE on AWS S3 bucket %s", bucketName)
 	}
 
 	terragruntOptions.Logger.Debugf("Enabled bucket-wide SSE on AWS S3 bucket %s", bucketName)
@@ -1205,7 +1205,7 @@ func checkIfSSEForS3Enabled(s3Client *s3.S3, config *ExtendedRemoteStateConfigS3
 	output, err := s3Client.GetBucketEncryption(input)
 	if err != nil {
 		terragruntOptions.Logger.Debugf("Error checking if SSE is enabled for AWS S3 bucket %s: %s", config.remoteStateConfigS3.Bucket, err.Error())
-		return false, err
+		return false, errors.WithStackTraceAndPrefix(err, "Error checking if SSE is enabled for AWS S3 bucket %s", config.remoteStateConfigS3.Bucket)
 	}
 
 	if output.ServerSideEncryptionConfiguration == nil {
@@ -1227,10 +1227,10 @@ func checkIfSSEForS3Enabled(s3Client *s3.S3, config *ExtendedRemoteStateConfigS3
 	return false, nil
 }
 
-// Enable bucket-wide Access Logging for the AWS S3 bucket specified in the given config
+// EnableAccessLoggingForS3BucketWide Enable bucket-wide Access Logging for the AWS S3 bucket specified in the given config
 func EnableAccessLoggingForS3BucketWide(s3Client *s3.S3, config *RemoteStateConfigS3, terragruntOptions *options.TerragruntOptions, logsBucket string, logsBucketPrefix string) error {
 	if err := configureBucketAccessLoggingAcl(s3Client, aws.String(logsBucket), terragruntOptions); err != nil {
-		return errors.WithStackTrace(err)
+		return errors.WithStackTraceAndPrefix(err, "Error configuring bucket access logging ACL on S3 bucket %s", config.Bucket)
 	}
 
 	terragruntOptions.Logger.Debugf("Putting bucket logging on S3 bucket %s with TargetBucket %s and TargetPrefix %s", config.Bucket, logsBucket, logsBucketPrefix)
@@ -1246,7 +1246,7 @@ func EnableAccessLoggingForS3BucketWide(s3Client *s3.S3, config *RemoteStateConf
 	}
 
 	if _, err := s3Client.PutBucketLogging(&loggingInput); err != nil {
-		return errors.WithStackTrace(err)
+		return errors.WithStackTraceAndPrefix(err, "Error enabling bucket-wide Access Logging on AWS S3 bucket %s", config.Bucket)
 	}
 
 	terragruntOptions.Logger.Debugf("Enabled bucket-wide Access Logging on AWS S3 bucket %s", config.Bucket)
@@ -1260,7 +1260,7 @@ func checkIfAccessLoggingForS3Enabled(s3Client *s3.S3, config *RemoteStateConfig
 	output, err := s3Client.GetBucketLogging(input)
 	if err != nil {
 		terragruntOptions.Logger.Debugf("Error checking if Access Logging is enabled for AWS S3 bucket %s: %s", config.Bucket, err.Error())
-		return false, err
+		return false, errors.WithStackTraceAndPrefix(err, "Error checking if Access Logging is enabled for AWS S3 bucket %s", config.Bucket)
 	}
 
 	if output.LoggingEnabled == nil {
@@ -1288,7 +1288,7 @@ func EnablePublicAccessBlockingForS3Bucket(s3Client *s3.S3, bucketName string, t
 	)
 
 	if err != nil {
-		return errors.WithStackTrace(err)
+		return errors.WithStackTraceAndPrefix(err, "Error blocking all public access to S3 bucket %s", bucketName)
 	}
 
 	terragruntOptions.Logger.Debugf("Blocked all public access to S3 bucket %s", bucketName)
@@ -1310,7 +1310,7 @@ func checkIfS3PublicAccessBlockingEnabled(s3Client *s3.S3, config *RemoteStateCo
 			}
 		}
 
-		return false, errors.WithStackTrace(err)
+		return false, errors.WithStackTraceAndPrefix(err, "Error checking if S3 bucket %s is configured to block public access", config.Bucket)
 	}
 
 	return validatePublicAccessBlock(output)
@@ -1351,7 +1351,7 @@ func configureBucketAccessLoggingAcl(s3Client *s3.S3, bucket *string, terragrunt
 	}
 
 	if _, err := s3Client.PutBucketAcl(&aclInput); err != nil {
-		return errors.WithStackTrace(err)
+		return errors.WithStackTraceAndPrefix(err, "Error granting WRITE and READ_ACP permissions to S3 Log Delivery (%s) for bucket %s", s3LogDeliveryGranteeUri, *bucket)
 	}
 
 	return waitUntilBucketHasAccessLoggingAcl(s3Client, bucket, terragruntOptions)
@@ -1365,7 +1365,7 @@ func waitUntilBucketHasAccessLoggingAcl(s3Client *s3.S3, bucket *string, terragr
 	for i := 0; i < maxRetries; i++ {
 		out, err := s3Client.GetBucketAcl(&s3.GetBucketAclInput{Bucket: bucket})
 		if err != nil {
-			return errors.WithStackTrace(err)
+			return errors.WithStackTraceAndPrefix(err, "Error getting ACL for bucket %s", *bucket)
 		}
 
 		hasReadAcp := false
@@ -1415,7 +1415,7 @@ func checkBucketAccess(s3Client *s3.S3, bucket *string, key *string) error {
 	if awsErr.Code() == "NoSuchBucket" || awsErr.Code() == "NoSuchKey" {
 		return nil
 	}
-	return err
+	return errors.WithStackTraceAndPrefix(err, "Error checking access to S3 bucket %s", *bucket)
 }
 
 // Create a table for locks in DynamoDB if the user has configured a lock table and the table doesn't already exist


Test patch:
diff --git a/test/integration_test.go b/test/integration_test.go
index 35ff00d5e4..f7ababdfba 100644
--- a/test/integration_test.go
+++ b/test/integration_test.go
@@ -6158,6 +6158,7 @@ func TestTerragruntPrintAwsErrors(t *testing.T) {
 	assert.Error(t, err)
 	message := fmt.Sprintf("%v", err.Error())
 	assert.True(t, strings.Contains(message, "AllAccessDisabled: All access to this object has been disabled") || strings.Contains(message, "BucketRegionError: incorrect region"))
+	assert.Contains(t, message, s3BucketName)
 }
 
 func TestTerragruntErrorWhenStateBucketIsInDifferentRegion(t *testing.T) {