LocalStack & Terraspace

Hi Community,

How do I configure my app.rb file to correctly create and connect to S3 using LocalStack as my testing platform? Thank you.

I have been testing with the following configs:

require 'aws-sdk-s3'
    region = "eu-west-1"
#    Aws.config.update(
#      endpoint:  'http://s3.localhost.localstack.cloud:4566', # update with localstack endpoint
#      access_key_id: 'fake', # update with localstack credentials
#      secret_access_key: 'fake', # update with localstack credentials
#      region: region,
#      force_path_style: true, # Enable 'force_path_style' => true, if bucket name is non DNS compliant
#    )

#    Aws.config.update(
#        endpoint:  'http://s3.localhost.localstack.cloud:4566', # update with localstack endpoint
#        credentials: Aws::Credentials.new('fake', 'fake'),
#        region: region,
#        force_path_style: true,
#      )


#    # Create an S3 client with custom endpoint and force path style
s3 = Aws::S3::Client.new(
    endpoint:  'http://s3.localhost.localstack.cloud:4566', # update with localstack endpoint
    region: region,
    access_key_id: 'fake', # update with localstack credentials
    secret_access_key: 'fake', # update with localstack credentials
    force_path_style: true  # Enable force path style
  )
  # Docs: https://terraspace.cloud/docs/config/reference/
Terraspace.configure do |config|
  config.logger.level = :info
  # copy_modules setting introduced 2.2.5 to speed up terraspace build
  # See: https://terraspace.cloud/docs/config/reference
  config.build.copy_modules = true
  #config.build.cache_dir	= ":APP/:ROLE/:ENV/:BUILD_DIR" # without :REGION
  #endpoint: 'http://localhost:4572',
  #credentials: Aws::Credentials.new('fake', 'fake'),
  #region: 'us-east-1',
  #force_path_style: true,
end


I am making progress by doing the following:

aws config

[profile localstack-dev]
endpoint_url = http://*.localgd:4566
region = eu-west-1
output = json

Terraspace

infra → config → provider.tf

provider "aws" {
  region                      = "eu-west-1"
  access_key                  = "fake"
  secret_key                  = "fake"
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true
  s3_force_path_style         = true

  endpoints {
    apigateway      = "http://localhost:4566"
    apigatewayv2    = "http://localhost:4566"
    cloudformation  = "http://localhost:4566"
    cloudwatch      = "http://localhost:4566"
    cognitoidp      = "http://localhost:4566"
    cognitosync     = "http://localhost:4566"
    cognitoidentity = "http://localhost:4566"
    dynamodb        = "http://localhost:4566"
    ec2             = "http://localhost:4566"
    es              = "http://localhost:4566"
    elasticache     = "http://localhost:4566"
    firehose        = "http://localhost:4566"
    glue            = "http://localhost:4566"
    iam             = "http://localhost:4566"
    kinesis         = "http://localhost:4566"
    lambda          = "http://localhost:4566"
    rds             = "http://localhost:4566"
    redshift        = "http://localhost:4566"
    route53         = "http://localhost:4566"
    s3              = "http://*.local.gd:4566"
    secretsmanager  = "http://localhost:4566"
    ses             = "http://localhost:4566"
    sns             = "http://localhost:4566"
    sqs             = "http://localhost:4566"
    ssm             = "http://localhost:4566"
    stepfunctions   = "http://localhost:4566"
    sts             = "http://localhost:4566"
  }

Just got to figure out how to fix the hostname / path style

Aws::Errors::NoSuchEndpointError: Encountered a `SocketError` while attempting to connect to:

  http://*.localgd:4566

This is typically the result of an invalid `:region` option or a
poorly formatted `:endpoint` option.

* Avoid configuring the `:endpoint` option directly. Endpoints are constructed
  from the `:region`. The `:endpoint` option is reserved for certain services
  or for connecting to non-standard test endpoints.

* Not every service is available in every region.

* Never suffix region names with availability zones.
  Use "us-east-1", not "us-east-1a"

Known AWS regions include (not specific to this service):

af-south-1
ap-east-1
ap-northeast-1
ap-northeast-2
ap-northeast-3
ap-south-1
ap-south-2
ap-southeast-1
ap-southeast-2
ap-southeast-3
ap-southeast-4
aws-global
ca-central-1
eu-central-1
eu-central-2
eu-north-1
eu-south-1
eu-south-2
eu-west-1
eu-west-2
eu-west-3
il-central-1
me-central-1
me-south-1
sa-east-1
us-east-1
us-east-2
us-west-1
us-west-2
aws-cn-global
cn-north-1
cn-northwest-1
aws-us-gov-global
us-gov-east-1
us-gov-west-1
aws-iso-global
us-iso-east-1
us-iso-west-1
aws-iso-b-global
us-isob-east-1

Error evaluating ERB template around line 6 of: /Users/tvl/Documents/repos/i1/tf-terraspace/infra/config/terraform/backend.tf:
 1 # This file was initially generated by terraspace_plugin_aws 0.6.1
 2 # Backend Config Variables Docs
 3 # https://terraspace.cloud/docs/config/backend/variables/
 4 terraform {
 5   backend "s3" {
 6     bucket         = "<%= expansion('terraform-state-:ACCOUNT-:REGION-:ENV') %>"
 7     key            = "<%= expansion(':PROJECT/:REGION/:APP/:ROLE/:ENV/:EXTRA/:BUILD_DIR/terraform.tfstate') %>"
 8     region         = "<%= expansion(':REGION') %>"
 9     dynamodb_table = "terraform_locks"
10   }
11 }

app.rb

require 'aws-sdk-s3'

Aws.config.update(
  endpoint: 'http://s3.localhost.localstack.cloud.local.gd:4566',
  credentials: Aws::SharedCredentials.new(profile_name: 'localstack-dev', access_key_id: 'fake', secret_access_key: 'fake'),
  s3: {
    http_wire_trace: true,
    force_path_style: true,
    ssl_verify_peer: false,
    validate_params: false # Skip credential validation
  }
  #validate_params: true,
  #skip_requesting_account_id: true,
  #skip_metadata_api_check: true,
  #s3: {force_path_style: 'true'},
  #s3: {http_wire_trace: 'true'},
)

# Docs: https://terraspace.cloud/docs/config/reference/
Terraspace.configure do |config|
  config.cloud.cost.enabled = false
  config.logger.level = :debug
  config.test_framework = "rspec"
  # copy_modules setting introduced 2.2.5 to speed up terraspace build
  # See: https://terraspace.cloud/docs/config/reference
  config.build.copy_modules = true
  config.build.cache_dir	= ":REGION/:APP/:ROLE/:ENV/:BUILD_DIR" # without :REGION
end

AWS config

[profile localstack-dev]
endpoint_url = http://local.gd:4566
service = localstack-s3
region = eu-west-1
output = json

[profile localstack-nonprod]
endpoint_url = http://local.gd:4566
service = localstack-s3
region = eu-west-1
output = json

[profile localstack-prod]
endpoint_url = http://local.gd:4566
service = localstack-s3
region = eu-west-1
output = json

[services s3-specific]
s3 =
  endpoint_url = http://s3.localhost.localstack.cloud.local.gd:4566

provider.tf

provider "aws" {
  region                      = "eu-west-1"
  access_key                  = "fake"
  secret_key                  = "fake"
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true
  s3_force_path_style         = true

  endpoints {
    apigateway      = "http://local.gd:4566"
    apigatewayv2    = "http://local.gd:4566"
    cloudformation  = "http://local.gd:4566"
    cloudwatch      = "http://local.gd:4566"
    cognitoidp      = "http://local.gd:4566"
    cognitosync     = "http://local.gd:4566"
    cognitoidentity = "http://local.gd:4566"
    dynamodb        = "http://local.gd:4566"
    ec2             = "http://local.gd:4566"
    es              = "http://local.gd:4566"
    elasticache     = "http://local.gd:4566"
    firehose        = "http://local.gd:4566"
    glue            = "http://local.gd:4566"
    iam             = "http://local.gd:4566"
    kinesis         = "http://local.gd:4566"
    lambda          = "http://local.gd:4566"
    rds             = "http://local.gd:4566"
    redshift        = "http://local.gd:4566"
    route53         = "http://local.gd:4566"
    s3              = "http://local.gd:4566"
    secretsmanager  = "http://local.gd:4566"
    ses             = "http://local.gd:4566"
    sns             = "http://local.gd:4566"
    sqs             = "http://local.gd:4566"
    ssm             = "http://local.gd:4566"
    stepfunctions   = "http://local.gd:4566"
    sts             = "http://local.gd:4566"
  }
}

Stuck here now

Initializing the backend...
Initializing modules...
╷
│ Error: error configuring S3 Backend: error validating provider credentials: error calling sts:GetCallerIdentity: InvalidClientTokenId: The security token included in the request is invalid.
│ 	status code: 403, request id: 05b6b1f9-2254-464c-ae97-c508b6d1aebc

My app.rb looks like this now

require 'aws-sdk-s3'

Aws.config.update(
  endpoint: 'https://localhost.localstack.cloud:4566',
  credentials: Aws::SharedCredentials.new(profile_name: 'localstack-dev', access_key_id: 'fake', secret_access_key: 'fake'),
  s3: {
    endpoint: 'https://s3.localhost.localstack.cloud:4566',
    access_key_id: 'fake',
    secret_access_key: 'fake',
    region: 'eu-west-1',
    http_wire_trace: true,
    force_path_style: true,
  }
)

# Docs: https://terraspace.cloud/docs/config/reference/
Terraspace.configure do |config|
  config.cloud.cost.enabled = false
  config.logger.level = :debug
  config.test_framework = "rspec"
  # copy_modules setting introduced 2.2.5 to speed up terraspace build
  # See: https://terraspace.cloud/docs/config/reference
  config.build.copy_modules = true
  config.build.cache_dir	= ":REGION/:APP/:ROLE/:ENV/:BUILD_DIR"
end```

And my AWS config looks like this

[profile localstack-nonprod]
endpoint_url = https://localhost.localstack.cloud:4566
services = localstack
region = eu-west-1
output = json
cli_history = enabled
cli_pager = bat

[profile localstack-prod]
endpoint_url = https://localhost.localstack.cloud:4566
services = localstack
region = eu-west-1
output = json
cli_history = enabled
cli_pager = bat

[services localstack]
s3 =
  addressing_style = path

And my bucket is created as you can see from here when I run terraspace up demo

aws s3 ls --profile localstack-dev
2023-10-09 16:40:33 terraform-state-000000000000-eu-west-1-dev

But I am stuck here.

Conn close
Bucket already exist: terraform-state-000000000000-eu-west-1-dev
opening connection to s3.localhost.localstack.cloud:4566...
opened
starting SSL for s3.localhost.localstack.cloud:4566...
SSL established, protocol: TLSv1.2, cipher: ECDHE-RSA-AES128-GCM-SHA256
<- "GET /terraform-state-000000000000-eu-west-1-dev/?tagging HTTP/1.1\r\nAccept-Encoding: \r\nUser-Agent: aws-sdk-ruby3/3.185.1 ua/2.0 api/s3#1.136.0 os/macos#21 md/x86_64 lang/ruby#3.0.3 md/3.0.3 cfg/retry-mode#legacy\r\nHost: s3.localhost.localstack.cloud:4566\r\nX-Amz-Date: 20231010T163438Z\r\nX-Amz-Content-Sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\r\nAuthorization: AWS4-HMAC-SHA256 Credential=fake/20231010/eu-west-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=c6c4beef839192b954840ada989d74ab0b9862b5b18fc011c53a4abe01d230e7\r\nAccept: */*\r\n\r\n"
-> "HTTP/1.1 404 \r\n"
-> "Content-Type: application/xml\r\n"
-> "Content-Length: 249\r\n"
-> "x-amz-request-id: ************************************\r\n"
-> "x-amz-id-2: s9lzHYrFp76ZVxRcpX9+5cjAnEH2ROuNkd2BHfIa6UkFVdtjf5mKR3/eTPFvsiP/XV/VLi31234=\r\n"
-> "Connection: close\r\n"
-> "date: Tue, 10 Oct 2023 16:34:38 GMT\r\n"
-> "server: hypercorn-h11\r\n"
-> "\r\n"
reading 249 bytes...
-> ""
-> "<?xml version='1.0' encoding='utf-8'?>\n<Error><Code>NoSuchTagSet</Code><Message>The TagSet does not exist</Message><RequestId>************************************</RequestId><BucketName>terraform-state-000000000000-eu-west-1-dev</BucketName></Error>"
read 249 bytes
Conn close
Table already exist: terraform_locks
=> terraform init -reconfigure -get -input=false

Initializing the backend...
Initializing modules...
╷
│ Error: error configuring S3 Backend: error validating provider credentials: error calling sts:GetCallerIdentity: InvalidClientTokenId: The security token included in the request is invalid.
│ 	status code: 403, request id: 8fbf3fdb-3d5d-4f74-8a67-16726b48253a
│