Repository: docker/compose-cli
Pull request #818 (2020-10-20 14:27:19)

Remove status truncate on wait
On errors, the event status text exceeds one line length and this breaks the progress writer output. 
We now removed the status truncation from wait and calculate the max length for the status text based on the terminal width in the progress writer.
At the end, we return the full error text.

```
$ docker compose up
[+] Running 10/10
 ⠿ hello                       DELETE_COMPLETE                                                                                          192.0s
 ⠿ Cluster                     DELETE_COMPLETE                                                                                          142.0s
 ⠿ DefaultNetwork              DELETE_COMPLETE                                                                                          142.0s
 ⠿ CloudMap                    DELETE_COMPLETE                                                                                          188.0s
 ⠿ LogGroup                    DELETE_COMPLETE                                                                                          144.0s
 ⠿ HelloTaskExecutionRole      DELETE_COMPLETE                                                                                          145.1s
 ⠿ DefaultNetworkIngress       DELETE_COMPLETE                                                                                           80.0s
 ⠿ HelloTaskDefinition         DELETE_COMPLETE                                                                                          124.0s
 ⠿ HelloServiceDiscoveryEntry  DELETE_COMPLETE                                                                                           94.0s
 ⠿ HelloService                DELETE_COMPLETE                                                                                           89.0s
HelloService TaskFailedToStart: CannotPullContainerError: failed to resolve ref "docker.io/ancaiordache/hello:latest": pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed

```
\test-ecs


Patch:
diff --git a/ecs/autoscaling.go b/ecs/autoscaling.go
index 0c8cf251..7a40938d 100644
--- a/ecs/autoscaling.go
+++ b/ecs/autoscaling.go
@@ -62,7 +62,7 @@ func (b *ecsAPIService) createAutoscalingPolicy(project *types.Project, resource
 	}
 
 	// Why isn't this just the service ARN ?????
-	resourceID := cloudformation.Join("/", []string{"service", resources.cluster.ID(), cloudformation.GetAtt(serviceResourceName(service.Name), "Name")})
+	resourceID := cloudformation.Join("/", []string{"service", resources.cluster, cloudformation.GetAtt(serviceResourceName(service.Name), "Name")})
 
 	target := fmt.Sprintf("%sScalableTarget", normalizeResourceName(service.Name))
 	template.Resources[target] = &applicationautoscaling.ScalableTarget{
diff --git a/ecs/aws.go b/ecs/aws.go
index 915c1902..b5852017 100644
--- a/ecs/aws.go
+++ b/ecs/aws.go
@@ -35,11 +35,11 @@ const (
 // API hides aws-go-sdk into a simpler, focussed API subset
 type API interface {
 	CheckRequirements(ctx context.Context, region string) error
-	ResolveCluster(ctx context.Context, nameOrArn string) (awsResource, error)
+	ClusterExists(ctx context.Context, name string) (bool, error)
 	CreateCluster(ctx context.Context, name string) (string, error)
 	CheckVPC(ctx context.Context, vpcID string) error
 	GetDefaultVPC(ctx context.Context) (string, error)
-	GetSubNets(ctx context.Context, vpcID string) ([]awsResource, error)
+	GetSubNets(ctx context.Context, vpcID string) ([]string, error)
 	GetRoleArn(ctx context.Context, name string) (string, error)
 	StackExists(ctx context.Context, name string) (bool, error)
 	CreateStack(ctx context.Context, name string, template []byte) error
@@ -66,14 +66,14 @@ type API interface {
 	getURLWithPortMapping(ctx context.Context, targetGroupArns []string) ([]compose.PortPublisher, error)
 	ListTasks(ctx context.Context, cluster string, family string) ([]string, error)
 	GetPublicIPs(ctx context.Context, interfaces ...string) (map[string]string, error)
-	ResolveLoadBalancer(ctx context.Context, nameOrArn string) (awsResource, string, error)
+	LoadBalancerType(ctx context.Context, arn string) (string, error)
 	GetLoadBalancerURL(ctx context.Context, arn string) (string, error)
 	GetParameter(ctx context.Context, name string) (string, error)
 	SecurityGroupExists(ctx context.Context, sg string) (bool, error)
 	DeleteCapacityProvider(ctx context.Context, arn string) error
 	DeleteAutoscalingGroup(ctx context.Context, arn string) error
-	ResolveFileSystem(ctx context.Context, id string) (awsResource, error)
-	FindFileSystem(ctx context.Context, tags map[string]string) (awsResource, error)
+	FileSystemExists(ctx context.Context, id string) (bool, error)
+	FindFileSystem(ctx context.Context, tags map[string]string) (string, error)
 	CreateFileSystem(ctx context.Context, tags map[string]string) (string, error)
 	DeleteFileSystem(ctx context.Context, id string) error
 }
diff --git a/ecs/awsResources.go b/ecs/awsResources.go
index 27c1374d..980329d8 100644
--- a/ecs/awsResources.go
+++ b/ecs/awsResources.go
@@ -38,13 +38,13 @@ import (
 
 // awsResources hold the AWS component being used or created to support services definition
 type awsResources struct {
-	vpc              string // shouldn't this also be an awsResource ?
-	subnets          []awsResource
-	cluster          awsResource
-	loadBalancer     awsResource
+	vpc              string
+	subnets          []string
+	cluster          string
+	loadBalancer     string
 	loadBalancerType string
 	securityGroups   map[string]string
-	filesystems      map[string]awsResource
+	filesystems      map[string]string
 }
 
 func (r *awsResources) serviceSecurityGroups(service types.ServiceConfig) []string {
@@ -63,63 +63,6 @@ func (r *awsResources) allSecurityGroups() []string {
 	return securityGroups
 }
 
-func (r *awsResources) subnetsIDs() []string {
-	var ids []string
-	for _, r := range r.subnets {
-		ids = append(ids, r.ID())
-	}
-	return ids
-}
-
-// awsResource is abstract representation for any (existing or future) AWS resource that we can refer both by ID or full ARN
-type awsResource interface {
-	ARN() string
-	ID() string
-}
-
-// existingAWSResource hold references to an existing AWS component
-type existingAWSResource struct {
-	arn string
-	id  string
-}
-
-func (r existingAWSResource) ARN() string {
-	return r.arn
-}
-
-func (r existingAWSResource) ID() string {
-	return r.id
-}
-
-// cloudformationResource hold references to a future AWS resource managed by CloudFormation
-// to be used by CloudFormation resources where Ref returns the Amazon Resource ID
-type cloudformationResource struct {
-	logicalName string
-}
-
-func (r cloudformationResource) ARN() string {
-	return cloudformation.GetAtt(r.logicalName, "Arn")
-}
-
-func (r cloudformationResource) ID() string {
-	return cloudformation.Ref(r.logicalName)
-}
-
-// cloudformationARNResource hold references to a future AWS resource managed by CloudFormation
-// to be used by CloudFormation resources where Ref returns the Amazon Resource Name (ARN)
-type cloudformationARNResource struct {
-	logicalName  string
-	nameProperty string
-}
-
-func (r cloudformationARNResource) ARN() string {
-	return cloudformation.Ref(r.logicalName)
-}
-
-func (r cloudformationARNResource) ID() string {
-	return cloudformation.GetAtt(r.logicalName, r.nameProperty)
-}
-
 // parse look into compose project for configured resource to use, and check they are valid
 func (b *ecsAPIService) parse(ctx context.Context, project *types.Project, template *cloudformation.Template) (awsResources, error) {
 	r := awsResources{}
@@ -147,28 +90,28 @@ func (b *ecsAPIService) parse(ctx context.Context, project *types.Project, templ
 	return r, nil
 }
 
-func (b *ecsAPIService) parseClusterExtension(ctx context.Context, project *types.Project, template *cloudformation.Template) (awsResource, error) {
+func (b *ecsAPIService) parseClusterExtension(ctx context.Context, project *types.Project, template *cloudformation.Template) (string, error) {
 	if x, ok := project.Extensions[extensionCluster]; ok {
-		nameOrArn := x.(string) // can be name _or_ ARN.
-		cluster, err := b.aws.ResolveCluster(ctx, nameOrArn)
+		cluster := x.(string)
+		ok, err := b.aws.ClusterExists(ctx, cluster)
 		if err != nil {
-			return nil, err
+			return "", err
 		}
 		if !ok {
-			return nil, errors.Wrapf(errdefs.ErrNotFound, "cluster %q does not exist", cluster)
+			return "", errors.Wrapf(errdefs.ErrNotFound, "cluster %q does not exist", cluster)
 		}
 
-		template.Metadata["Cluster"] = cluster.ARN()
+		template.Metadata["Cluster"] = cluster
 		return cluster, nil
 	}
-	return nil, nil
+	return "", nil
 }
 
-func (b *ecsAPIService) parseVPCExtension(ctx context.Context, project *types.Project) (string, []awsResource, error) {
+func (b *ecsAPIService) parseVPCExtension(ctx context.Context, project *types.Project) (string, []string, error) {
 	var vpc string
 	if x, ok := project.Extensions[extensionVPC]; ok {
-		vpcID := x.(string)
-		err := b.aws.CheckVPC(ctx, vpcID)
+		vpc = x.(string)
+		err := b.aws.CheckVPC(ctx, vpc)
 		if err != nil {
 			return "", nil, err
 		}
@@ -191,22 +134,22 @@ func (b *ecsAPIService) parseVPCExtension(ctx context.Context, project *types.Pr
 	return vpc, subNets, nil
 }
 
-func (b *ecsAPIService) parseLoadBalancerExtension(ctx context.Context, project *types.Project) (awsResource, string, error) {
+func (b *ecsAPIService) parseLoadBalancerExtension(ctx context.Context, project *types.Project) (string, string, error) {
 	if x, ok := project.Extensions[extensionLoadBalancer]; ok {
-		nameOrArn := x.(string)
-		loadBalancer, loadBalancerType, err := b.aws.ResolveLoadBalancer(ctx, nameOrArn)
+		loadBalancer := x.(string)
+		loadBalancerType, err := b.aws.LoadBalancerType(ctx, loadBalancer)
 		if err != nil {
-			return nil, "", err
+			return "", "", err
 		}
 
 		required := getRequiredLoadBalancerType(project)
 		if loadBalancerType != required {
-			return nil, "", fmt.Errorf("load balancer %q is of type %s, project require a %s", nameOrArn, loadBalancerType, required)
+			return "", "", fmt.Errorf("load balancer %s is of type %s, project require a %s", loadBalancer, loadBalancerType, required)
 		}
 
-		return loadBalancer, loadBalancerType, err
+		return loadBalancer, loadBalancerType, nil
 	}
-	return nil, "", nil
+	return "", "", nil
 }
 
 func (b *ecsAPIService) parseExternalNetworks(ctx context.Context, project *types.Project) (map[string]string, error) {
@@ -236,15 +179,18 @@ func (b *ecsAPIService) parseExternalNetworks(ctx context.Context, project *type
 	return securityGroups, nil
 }
 
-func (b *ecsAPIService) parseExternalVolumes(ctx context.Context, project *types.Project) (map[string]awsResource, error) {
-	filesystems := make(map[string]awsResource, len(project.Volumes))
+func (b *ecsAPIService) parseExternalVolumes(ctx context.Context, project *types.Project) (map[string]string, error) {
+	filesystems := make(map[string]string, len(project.Volumes))
 	for name, vol := range project.Volumes {
 		if vol.External.External {
-			arn, err := b.aws.ResolveFileSystem(ctx, vol.Name)
+			exists, err := b.aws.FileSystemExists(ctx, vol.Name)
 			if err != nil {
 				return nil, err
 			}
-			filesystems[name] = arn
+			if !exists {
+				return nil, errors.Wrapf(errdefs.ErrNotFound, "EFS file system %q doesn't exist", vol.Name)
+			}
+			filesystems[name] = vol.Name
 			continue
 		}
 
@@ -253,12 +199,12 @@ func (b *ecsAPIService) parseExternalVolumes(ctx context.Context, project *types
 			compose.ProjectTag: project.Name,
 			compose.VolumeTag:  name,
 		}
-		fileSystem, err := b.aws.FindFileSystem(ctx, tags)
+		id, err := b.aws.FindFileSystem(ctx, tags)
 		if err != nil {
 			return nil, err
 		}
-		if fileSystem != nil {
-			filesystems[name] = fileSystem
+		if id != "" {
+			filesystems[name] = id
 		}
 	}
 	return filesystems, nil
@@ -277,14 +223,14 @@ func (b *ecsAPIService) ensureResources(resources *awsResources, project *types.
 }
 
 func (b *ecsAPIService) ensureCluster(r *awsResources, project *types.Project, template *cloudformation.Template) {
-	if r.cluster != nil {
+	if r.cluster != "" {
 		return
 	}
 	template.Resources["Cluster"] = &ecs.Cluster{
 		ClusterName: project.Name,
 		Tags:        projectTags(project),
 	}
-	r.cluster = cloudformationResource{logicalName: "Cluster"}
+	r.cluster = cloudformation.Ref("Cluster")
 }
 
 func (b *ecsAPIService) ensureNetworks(r *awsResources, project *types.Project, template *cloudformation.Template) {
@@ -373,13 +319,13 @@ func (b *ecsAPIService) ensureVolumes(r *awsResources, project *types.Project, t
 			ThroughputMode:                  throughputMode,
 			AWSCloudFormationDeletionPolicy: "Retain",
 		}
-		r.filesystems[name] = cloudformationResource{logicalName: n}
+		r.filesystems[name] = cloudformation.Ref(n)
 	}
 	return nil
 }
 
 func (b *ecsAPIService) ensureLoadBalancer(r *awsResources, project *types.Project, template *cloudformation.Template) {
-	if r.loadBalancer != nil {
+	if r.loadBalancer != "" {
 		return
 	}
 	if allServices(project.Services, func(it types.ServiceConfig) bool {
@@ -399,14 +345,11 @@ func (b *ecsAPIService) ensureLoadBalancer(r *awsResources, project *types.Proje
 	template.Resources["LoadBalancer"] = &elasticloadbalancingv2.LoadBalancer{
 		Scheme:         elbv2.LoadBalancerSchemeEnumInternetFacing,
 		SecurityGroups: securityGroups,
-		Subnets:        r.subnetsIDs(),
+		Subnets:        r.subnets,
 		Tags:           projectTags(project),
 		Type:           balancerType,
 	}
-	r.loadBalancer = cloudformationARNResource{
-		logicalName:  "LoadBalancer",
-		nameProperty: "LoadBalancerName",
-	}
+	r.loadBalancer = cloudformation.Ref("LoadBalancer")
 	r.loadBalancerType = balancerType
 }
 
diff --git a/ecs/aws_mock.go b/ecs/aws_mock.go
index 5c669cb5..504de0c5 100644
--- a/ecs/aws_mock.go
+++ b/ecs/aws_mock.go
@@ -6,13 +6,12 @@ package ecs
 
 import (
 	context "context"
-	reflect "reflect"
-
 	cloudformation "github.com/aws/aws-sdk-go/service/cloudformation"
 	ecs "github.com/aws/aws-sdk-go/service/ecs"
 	compose "github.com/docker/compose-cli/api/compose"
 	secrets "github.com/docker/compose-cli/api/secrets"
 	gomock "github.com/golang/mock/gomock"
+	reflect "reflect"
 )
 
 // MockAPI is a mock of API interface
@@ -66,6 +65,21 @@ func (mr *MockAPIMockRecorder) CheckVPC(arg0, arg1 interface{}) *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckVPC", reflect.TypeOf((*MockAPI)(nil).CheckVPC), arg0, arg1)
 }
 
+// ClusterExists mocks base method
+func (m *MockAPI) ClusterExists(arg0 context.Context, arg1 string) (bool, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "ClusterExists", arg0, arg1)
+	ret0, _ := ret[0].(bool)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// ClusterExists indicates an expected call of ClusterExists
+func (mr *MockAPIMockRecorder) ClusterExists(arg0, arg1 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterExists", reflect.TypeOf((*MockAPI)(nil).ClusterExists), arg0, arg1)
+}
+
 // CreateChangeSet mocks base method
 func (m *MockAPI) CreateChangeSet(arg0 context.Context, arg1 string, arg2 []byte) (string, error) {
 	m.ctrl.T.Helper()
@@ -240,11 +254,26 @@ func (mr *MockAPIMockRecorder) DescribeStackEvents(arg0, arg1 interface{}) *gomo
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeStackEvents", reflect.TypeOf((*MockAPI)(nil).DescribeStackEvents), arg0, arg1)
 }
 
+// FileSystemExists mocks base method
+func (m *MockAPI) FileSystemExists(arg0 context.Context, arg1 string) (bool, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "FileSystemExists", arg0, arg1)
+	ret0, _ := ret[0].(bool)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// FileSystemExists indicates an expected call of FileSystemExists
+func (mr *MockAPIMockRecorder) FileSystemExists(arg0, arg1 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FileSystemExists", reflect.TypeOf((*MockAPI)(nil).FileSystemExists), arg0, arg1)
+}
+
 // FindFileSystem mocks base method
-func (m *MockAPI) FindFileSystem(arg0 context.Context, arg1 map[string]string) (awsResource, error) {
+func (m *MockAPI) FindFileSystem(arg0 context.Context, arg1 map[string]string) (string, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "FindFileSystem", arg0, arg1)
-	ret0, _ := ret[0].(awsResource)
+	ret0, _ := ret[0].(string)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
@@ -410,10 +439,10 @@ func (mr *MockAPIMockRecorder) GetStackID(arg0, arg1 interface{}) *gomock.Call {
 }
 
 // GetSubNets mocks base method
-func (m *MockAPI) GetSubNets(arg0 context.Context, arg1 string) ([]awsResource, error) {
+func (m *MockAPI) GetSubNets(arg0 context.Context, arg1 string) ([]string, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetSubNets", arg0, arg1)
-	ret0, _ := ret[0].([]awsResource)
+	ret0, _ := ret[0].([]string)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
@@ -544,50 +573,19 @@ func (mr *MockAPIMockRecorder) ListTasks(arg0, arg1, arg2 interface{}) *gomock.C
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTasks", reflect.TypeOf((*MockAPI)(nil).ListTasks), arg0, arg1, arg2)
 }
 
-// ResolveCluster mocks base method
-func (m *MockAPI) ResolveCluster(arg0 context.Context, arg1 string) (awsResource, error) {
+// LoadBalancerType mocks base method
+func (m *MockAPI) LoadBalancerType(arg0 context.Context, arg1 string) (string, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "ResolveCluster", arg0, arg1)
-	ret0, _ := ret[0].(awsResource)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// ResolveCluster indicates an expected call of ResolveCluster
-func (mr *MockAPIMockRecorder) ResolveCluster(arg0, arg1 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveCluster", reflect.TypeOf((*MockAPI)(nil).ResolveCluster), arg0, arg1)
-}
-
-// ResolveFileSystem mocks base method
-func (m *MockAPI) ResolveFileSystem(arg0 context.Context, arg1 string) (awsResource, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "ResolveFileSystem", arg0, arg1)
-	ret0, _ := ret[0].(awsResource)
+	ret := m.ctrl.Call(m, "LoadBalancerType", arg0, arg1)
+	ret0, _ := ret[0].(string)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
-// ResolveFileSystem indicates an expected call of ResolveFileSystem
-func (mr *MockAPIMockRecorder) ResolveFileSystem(arg0, arg1 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveFileSystem", reflect.TypeOf((*MockAPI)(nil).ResolveFileSystem), arg0, arg1)
-}
-
-// ResolveLoadBalancer mocks base method
-func (m *MockAPI) ResolveLoadBalancer(arg0 context.Context, arg1 string) (awsResource, string, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "ResolveLoadBalancer", arg0, arg1)
-	ret0, _ := ret[0].(awsResource)
-	ret1, _ := ret[1].(string)
-	ret2, _ := ret[2].(error)
-	return ret0, ret1, ret2
-}
-
-// ResolveLoadBalancer indicates an expected call of ResolveLoadBalancer
-func (mr *MockAPIMockRecorder) ResolveLoadBalancer(arg0, arg1 interface{}) *gomock.Call {
+// LoadBalancerType indicates an expected call of LoadBalancerType
+func (mr *MockAPIMockRecorder) LoadBalancerType(arg0, arg1 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveLoadBalancer", reflect.TypeOf((*MockAPI)(nil).ResolveLoadBalancer), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadBalancerType", reflect.TypeOf((*MockAPI)(nil).LoadBalancerType), arg0, arg1)
 }
 
 // SecurityGroupExists mocks base method
diff --git a/ecs/backend.go b/ecs/backend.go
index 70155729..1f3a6b1c 100644
--- a/ecs/backend.go
+++ b/ecs/backend.go
@@ -19,6 +19,9 @@ package ecs
 import (
 	"context"
 
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/session"
+
 	"github.com/docker/compose-cli/api/compose"
 	"github.com/docker/compose-cli/api/containers"
 	"github.com/docker/compose-cli/api/resources"
@@ -29,9 +32,6 @@ import (
 	"github.com/docker/compose-cli/context/cloud"
 	"github.com/docker/compose-cli/context/store"
 	"github.com/docker/compose-cli/errdefs"
-
-	"github.com/aws/aws-sdk-go/aws"
-	"github.com/aws/aws-sdk-go/aws/session"
 )
 
 const backendType = store.EcsContextType
diff --git a/ecs/cloudformation.go b/ecs/cloudformation.go
index 58dbaaea..e284dd51 100644
--- a/ecs/cloudformation.go
+++ b/ecs/cloudformation.go
@@ -77,8 +77,6 @@ func (b *ecsAPIService) convert(ctx context.Context, project *types.Project) (*c
 
 	b.createNFSMountTarget(project, resources, template)
 
-	b.createAccessPoints(project, resources, template)
-
 	for _, service := range project.Services {
 		err := b.createService(project, service, template, resources)
 		if err != nil {
@@ -98,7 +96,7 @@ func (b *ecsAPIService) convert(ctx context.Context, project *types.Project) (*c
 
 func (b *ecsAPIService) createService(project *types.Project, service types.ServiceConfig, template *cloudformation.Template, resources awsResources) error {
 	taskExecutionRole := b.createTaskExecutionRole(project, service, template)
-	taskRole := b.createTaskRole(project, service, template, resources)
+	taskRole := b.createTaskRole(project, service, template)
 
 	definition, err := b.createTaskDefinition(project, service, resources)
 	if err != nil {
@@ -168,7 +166,7 @@ func (b *ecsAPIService) createService(project *types.Project, service types.Serv
 
 	template.Resources[serviceResourceName(service.Name)] = &ecs.Service{
 		AWSCloudFormationDependsOn: dependsOn,
-		Cluster:                    resources.cluster.ARN(),
+		Cluster:                    resources.cluster,
 		DesiredCount:               desiredCount,
 		DeploymentController: &ecs.Service_DeploymentController{
 			Type: ecsapi.DeploymentControllerTypeEcs,
@@ -184,7 +182,7 @@ func (b *ecsAPIService) createService(project *types.Project, service types.Serv
 			AwsvpcConfiguration: &ecs.Service_AwsVpcConfiguration{
 				AssignPublicIp: assignPublicIP,
 				SecurityGroups: resources.serviceSecurityGroups(service),
-				Subnets:        resources.subnetsIDs(),
+				Subnets:        resources.subnets,
 			},
 		},
 		PlatformVersion:    platformVersion,
@@ -289,7 +287,7 @@ func computeRollingUpdateLimits(service types.ServiceConfig) (int, int, error) {
 
 func (b *ecsAPIService) createListener(service types.ServiceConfig, port types.ServicePortConfig,
 	template *cloudformation.Template,
-	targetGroupName string, loadBalancer awsResource, protocol string) string {
+	targetGroupName string, loadBalancerARN string, protocol string) string {
 	listenerName := fmt.Sprintf(
 		"%s%s%dListener",
 		normalizeResourceName(service.Name),
@@ -311,7 +309,7 @@ func (b *ecsAPIService) createListener(service types.ServiceConfig, port types.S
 				Type: elbv2.ActionTypeEnumForward,
 			},
 		},
-		LoadBalancerArn: loadBalancer.ARN(),
+		LoadBalancerArn: loadBalancerARN,
 		Protocol:        protocol,
 		Port:            int(port.Target),
 	}
@@ -377,21 +375,14 @@ func (b *ecsAPIService) createTaskExecutionRole(project *types.Project, service
 	return taskExecutionRole
 }
 
-func (b *ecsAPIService) createTaskRole(project *types.Project, service types.ServiceConfig, template *cloudformation.Template, resources awsResources) string {
+func (b *ecsAPIService) createTaskRole(project *types.Project, service types.ServiceConfig, template *cloudformation.Template) string {
 	taskRole := fmt.Sprintf("%sTaskRole", normalizeResourceName(service.Name))
 	rolePolicies := []iam.Role_Policy{}
 	if roles, ok := service.Extensions[extensionRole]; ok {
 		rolePolicies = append(rolePolicies, iam.Role_Policy{
-			PolicyName:     fmt.Sprintf("%s%sPolicy", normalizeResourceName(project.Name), normalizeResourceName(service.Name)),
 			PolicyDocument: roles,
 		})
 	}
-	for _, vol := range service.Volumes {
-		rolePolicies = append(rolePolicies, iam.Role_Policy{
-			PolicyName:     fmt.Sprintf("%s%sVolumeMountPolicy", normalizeResourceName(project.Name), normalizeResourceName(service.Name)),
-			PolicyDocument: volumeMountPolicyDocument(vol.Source, resources.filesystems[vol.Source].ARN()),
-		})
-	}
 	managedPolicies := []string{}
 	if v, ok := service.Extensions[extensionManagedPolicies]; ok {
 		for _, s := range v.([]interface{}) {
diff --git a/ecs/convert.go b/ecs/convert.go
index aef6007f..436e0bb3 100644
--- a/ecs/convert.go
+++ b/ecs/convert.go
@@ -81,15 +81,11 @@ func (b *ecsAPIService) createTaskDefinition(project *types.Project, service typ
 	}
 
 	for _, v := range service.Volumes {
-		n := fmt.Sprintf("%sAccessPoint", normalizeResourceName(v.Source))
+		volume := project.Volumes[v.Source]
 		volumes = append(volumes, ecs.TaskDefinition_Volume{
 			EFSVolumeConfiguration: &ecs.TaskDefinition_EFSVolumeConfiguration{
-				AuthorizationConfig: &ecs.TaskDefinition_AuthorizationConfig{
-					AccessPointId: cloudformation.Ref(n),
-					IAM:           "ENABLED",
-				},
-				FilesystemId:      resources.filesystems[v.Source].ID(),
-				TransitEncryption: "ENABLED",
+				FilesystemId:  resources.filesystems[v.Source],
+				RootDirectory: volume.DriverOpts["root_directory"],
 			},
 			Name: v.Source,
 		})
diff --git a/ecs/ec2.go b/ecs/ec2.go
index 8556135c..7438598a 100644
--- a/ecs/ec2.go
+++ b/ecs/ec2.go
@@ -83,7 +83,7 @@ func (b *ecsAPIService) createCapacityProvider(ctx context.Context, project *typ
 		LaunchConfigurationName: cloudformation.Ref("LaunchConfiguration"),
 		MaxSize:                 "10", //TODO
 		MinSize:                 "1",
-		VPCZoneIdentifier:       resources.subnetsIDs(),
+		VPCZoneIdentifier:       resources.subnets,
 	}
 
 	userData := base64.StdEncoding.EncodeToString([]byte(
diff --git a/ecs/iam.go b/ecs/iam.go
index f6b80ed2..5b061172 100644
--- a/ecs/iam.go
+++ b/ecs/iam.go
@@ -16,12 +16,6 @@
 
 package ecs
 
-import (
-	"fmt"
-
-	"github.com/awslabs/goformation/v4/cloudformation"
-)
-
 const (
 	ecsTaskExecutionPolicy = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
 	ecrReadOnlyPolicy      = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
@@ -55,31 +49,7 @@ func policyDocument(service string) PolicyDocument {
 			},
 		},
 	}
-}
 
-func volumeMountPolicyDocument(volume string, filesystem string) PolicyDocument {
-	ap := fmt.Sprintf("%sAccessPoint", normalizeResourceName(volume))
-	return PolicyDocument{
-		Version: "2012-10-17", // https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_version.html
-		Statement: []PolicyStatement{
-			{
-				Effect: "Allow",
-				Resource: []string{
-					filesystem,
-				},
-				Action: []string{
-					"elasticfilesystem:ClientMount",
-					"elasticfilesystem:ClientWrite",
-					"elasticfilesystem:ClientRootAccess",
-				},
-				Condition: Condition{
-					StringEquals: map[string]string{
-						"elasticfilesystem:AccessPointArn": cloudformation.Ref(ap),
-					},
-				},
-			},
-		},
-	}
 }
 
 // PolicyDocument describes an IAM policy document
@@ -95,16 +65,9 @@ type PolicyStatement struct {
 	Action    []string        `json:",omitempty"`
 	Principal PolicyPrincipal `json:",omitempty"`
 	Resource  []string        `json:",omitempty"`
-	Condition Condition       `json:",omitempty"`
 }
 
 // PolicyPrincipal describes an IAM policy principal
 type PolicyPrincipal struct {
 	Service string `json:",omitempty"`
 }
-
-// Condition is the map of all conditions in the statement entry.
-type Condition struct {
-	StringEquals map[string]string `json:",omitempty"`
-	Bool         map[string]string `json:",omitempty"`
-}
diff --git a/ecs/sdk.go b/ecs/sdk.go
index a0e67ed7..6fd8bb18 100644
--- a/ecs/sdk.go
+++ b/ecs/sdk.go
@@ -29,7 +29,6 @@ import (
 	"github.com/docker/compose-cli/internal"
 
 	"github.com/aws/aws-sdk-go/aws"
-	"github.com/aws/aws-sdk-go/aws/arn"
 	"github.com/aws/aws-sdk-go/aws/request"
 	"github.com/aws/aws-sdk-go/aws/session"
 	"github.com/aws/aws-sdk-go/service/autoscaling"
@@ -108,22 +107,15 @@ func (s sdk) CheckRequirements(ctx context.Context, region string) error {
 	return nil
 }
 
-func (s sdk) ResolveCluster(ctx context.Context, nameOrArn string) (awsResource, error) {
-	logrus.Debug("CheckRequirements if cluster was already created: ", nameOrArn)
+func (s sdk) ClusterExists(ctx context.Context, name string) (bool, error) {
+	logrus.Debug("CheckRequirements if cluster was already created: ", name)
 	clusters, err := s.ECS.DescribeClustersWithContext(ctx, &ecs.DescribeClustersInput{
-		Clusters: []*string{aws.String(nameOrArn)},
+		Clusters: []*string{aws.String(name)},
 	})
 	if err != nil {
-		return nil, err
-	}
-	if len(clusters.Clusters) == 0 {
-		return nil, errors.Wrapf(errdefs.ErrNotFound, "cluster %q does not exist", nameOrArn)
+		return false, err
 	}
-	it := clusters.Clusters[0]
-	return existingAWSResource{
-		arn: aws.StringValue(it.ClusterArn),
-		id:  aws.StringValue(it.ClusterName),
-	}, nil
+	return len(clusters.Clusters) > 0, nil
 }
 
 func (s sdk) CreateCluster(ctx context.Context, name string) (string, error) {
@@ -147,7 +139,7 @@ func (s sdk) CheckVPC(ctx context.Context, vpcID string) error {
 	if !*output.EnableDnsSupport.Value {
 		return fmt.Errorf("VPC %q doesn't have DNS resolution enabled", vpcID)
 	}
-	return nil
+	return err
 }
 
 func (s sdk) GetDefaultVPC(ctx context.Context) (string, error) {
@@ -169,7 +161,7 @@ func (s sdk) GetDefaultVPC(ctx context.Context) (string, error) {
 	return *vpcs.Vpcs[0].VpcId, nil
 }
 
-func (s sdk) GetSubNets(ctx context.Context, vpcID string) ([]awsResource, error) {
+func (s sdk) GetSubNets(ctx context.Context, vpcID string) ([]string, error) {
 	logrus.Debug("Retrieve SubNets")
 	subnets, err := s.EC2.DescribeSubnetsWithContext(ctx, &ec2.DescribeSubnetsInput{
 		DryRun: nil,
@@ -184,12 +176,9 @@ func (s sdk) GetSubNets(ctx context.Context, vpcID string) ([]awsResource, error
 		return nil, err
 	}
 
-	ids := []awsResource{}
+	ids := []string{}
 	for _, subnet := range subnets.Subnets {
-		ids = append(ids, existingAWSResource{
-			arn: aws.StringValue(subnet.SubnetArn),
-			id:  aws.StringValue(subnet.SubnetId),
-		})
+		ids = append(ids, *subnet.SubnetId)
 	}
 	return ids, nil
 }
@@ -795,31 +784,18 @@ func (s sdk) GetPublicIPs(ctx context.Context, interfaces ...string) (map[string
 	return publicIPs, nil
 }
 
-func (s sdk) ResolveLoadBalancer(ctx context.Context, nameOrarn string) (awsResource, string, error) {
-	logrus.Debug("Check if LoadBalancer exists: ", nameOrarn)
-	var arns []*string
-	var names []*string
-	if arn.IsARN(nameOrarn) {
-		arns = append(arns, aws.String(nameOrarn))
-	} else {
-		names = append(names, aws.String(nameOrarn))
-	}
-
+func (s sdk) LoadBalancerType(ctx context.Context, arn string) (string, error) {
+	logrus.Debug("Check if LoadBalancer exists: ", arn)
 	lbs, err := s.ELB.DescribeLoadBalancersWithContext(ctx, &elbv2.DescribeLoadBalancersInput{
-		LoadBalancerArns: arns,
-		Names:            names,
+		LoadBalancerArns: []*string{aws.String(arn)},
 	})
 	if err != nil {
-		return nil, "", err
+		return "", err
 	}
 	if len(lbs.LoadBalancers) == 0 {
-		return nil, "", errors.Wrapf(errdefs.ErrNotFound, "load balancer %q does not exist", nameOrarn)
+		return "", fmt.Errorf("load balancer does not exist: %s", arn)
 	}
-	it := lbs.LoadBalancers[0]
-	return existingAWSResource{
-		arn: aws.StringValue(it.LoadBalancerArn),
-		id:  aws.StringValue(it.LoadBalancerName),
-	}, aws.StringValue(it.Type), nil
+	return aws.StringValue(lbs.LoadBalancers[0].Type), nil
 }
 
 func (s sdk) GetLoadBalancerURL(ctx context.Context, arn string) (string, error) {
@@ -887,42 +863,32 @@ func (s sdk) DeleteAutoscalingGroup(ctx context.Context, arn string) error {
 	return err
 }
 
-func (s sdk) ResolveFileSystem(ctx context.Context, id string) (awsResource, error) {
+func (s sdk) FileSystemExists(ctx context.Context, id string) (bool, error) {
 	desc, err := s.EFS.DescribeFileSystemsWithContext(ctx, &efs.DescribeFileSystemsInput{
 		FileSystemId: aws.String(id),
 	})
 	if err != nil {
-		return nil, err
-	}
-	if len(desc.FileSystems) == 0 {
-		return nil, errors.Wrapf(errdefs.ErrNotFound, "EFS file system %q doesn't exist", id)
+		return false, err
 	}
-	it := desc.FileSystems[0]
-	return existingAWSResource{
-		arn: aws.StringValue(it.FileSystemArn),
-		id:  aws.StringValue(it.FileSystemId),
-	}, nil
+	return len(desc.FileSystems) > 0, nil
 }
 
-func (s sdk) FindFileSystem(ctx context.Context, tags map[string]string) (awsResource, error) {
+func (s sdk) FindFileSystem(ctx context.Context, tags map[string]string) (string, error) {
 	var token *string
 	for {
 		desc, err := s.EFS.DescribeFileSystemsWithContext(ctx, &efs.DescribeFileSystemsInput{
 			Marker: token,
 		})
 		if err != nil {
-			return nil, err
+			return "", err
 		}
 		for _, filesystem := range desc.FileSystems {
 			if containsAll(filesystem.Tags, tags) {
-				return existingAWSResource{
-					arn: aws.StringValue(filesystem.FileSystemArn),
-					id:  aws.StringValue(filesystem.FileSystemId),
-				}, nil
+				return aws.StringValue(filesystem.FileSystemId), nil
 			}
 		}
 		if desc.NextMarker == token {
-			return nil, nil
+			return "", nil
 		}
 		token = desc.NextMarker
 	}
diff --git a/ecs/volumes.go b/ecs/volumes.go
index 939ea9f7..a5d4ea3a 100644
--- a/ecs/volumes.go
+++ b/ecs/volumes.go
@@ -19,8 +19,6 @@ package ecs
 import (
 	"fmt"
 
-	"github.com/docker/compose-cli/api/compose"
-
 	"github.com/awslabs/goformation/v4/cloudformation"
 	"github.com/awslabs/goformation/v4/cloudformation/efs"
 	"github.com/compose-spec/compose-go/types"
@@ -29,11 +27,11 @@ import (
 func (b *ecsAPIService) createNFSMountTarget(project *types.Project, resources awsResources, template *cloudformation.Template) {
 	for volume := range project.Volumes {
 		for _, subnet := range resources.subnets {
-			name := fmt.Sprintf("%sNFSMountTargetOn%s", normalizeResourceName(volume), normalizeResourceName(subnet.ID()))
+			name := fmt.Sprintf("%sNFSMountTargetOn%s", normalizeResourceName(volume), normalizeResourceName(subnet))
 			template.Resources[name] = &efs.MountTarget{
-				FileSystemId:   resources.filesystems[volume].ID(),
+				FileSystemId:   resources.filesystems[volume],
 				SecurityGroups: resources.allSecurityGroups(),
-				SubnetId:       subnet.ID(),
+				SubnetId:       subnet,
 			}
 		}
 	}
@@ -42,58 +40,7 @@ func (b *ecsAPIService) createNFSMountTarget(project *types.Project, resources a
 func (b *ecsAPIService) mountTargets(volume string, resources awsResources) []string {
 	var refs []string
 	for _, subnet := range resources.subnets {
-		refs = append(refs, fmt.Sprintf("%sNFSMountTargetOn%s", normalizeResourceName(volume), normalizeResourceName(subnet.ID())))
+		refs = append(refs, fmt.Sprintf("%sNFSMountTargetOn%s", normalizeResourceName(volume), normalizeResourceName(subnet)))
 	}
 	return refs
 }
-
-func (b *ecsAPIService) createAccessPoints(project *types.Project, r awsResources, template *cloudformation.Template) {
-	for name, volume := range project.Volumes {
-		n := fmt.Sprintf("%sAccessPoint", normalizeResourceName(name))
-
-		uid := volume.DriverOpts["uid"]
-		gid := volume.DriverOpts["gid"]
-		permissions := volume.DriverOpts["permissions"]
-		path := volume.DriverOpts["root_directory"]
-
-		ap := efs.AccessPoint{
-			AccessPointTags: []efs.AccessPoint_AccessPointTag{
-				{
-					Key:   compose.ProjectTag,
-					Value: project.Name,
-				},
-				{
-					Key:   compose.VolumeTag,
-					Value: name,
-				},
-				{
-					Key:   "Name",
-					Value: fmt.Sprintf("%s_%s", project.Name, name),
-				},
-			},
-			FileSystemId: r.filesystems[name].ID(),
-		}
-
-		if uid != "" {
-			ap.PosixUser = &efs.AccessPoint_PosixUser{
-				Uid: uid,
-				Gid: gid,
-			}
-		}
-		if path != "" {
-			root := efs.AccessPoint_RootDirectory{
-				Path: path,
-			}
-			ap.RootDirectory = &root
-			if uid != "" {
-				root.CreationInfo = &efs.AccessPoint_CreationInfo{
-					OwnerUid:    uid,
-					OwnerGid:    gid,
-					Permissions: permissions,
-				}
-			}
-		}
-
-		template.Resources[n] = &ap
-	}
-}
diff --git a/ecs/wait.go b/ecs/wait.go
index baedda0b..e0080062 100644
--- a/ecs/wait.go
+++ b/ecs/wait.go
@@ -74,8 +74,7 @@ func (b *ecsAPIService) WaitStackCompletion(ctx context.Context, name string, op
 			knownEvents[*event.EventId] = struct{}{}
 
 			resource := aws.StringValue(event.LogicalResourceId)
-			reason := shortenMessage(
-				aws.StringValue(event.ResourceStatusReason))
+			reason := aws.StringValue(event.ResourceStatusReason)
 			status := aws.StringValue(event.ResourceStatus)
 			progressStatus := progress.Working
 
@@ -116,21 +115,13 @@ func (b *ecsAPIService) WaitStackCompletion(ctx context.Context, name string, op
 			}
 			stackErr = err
 			operation = stackDelete
-			reason := shortenMessage(err.Error())
 			w.Event(progress.Event{
 				ID:         name,
 				Status:     progress.Error,
-				StatusText: reason,
+				StatusText: err.Error(),
 			})
 		}
 	}
 
 	return stackErr
 }
-
-func shortenMessage(message string) string {
-	if len(message) < 30 {
-		return message
-	}
-	return message[:30] + "..."
-}
diff --git a/progress/tty.go b/progress/tty.go
index 1da062ae..9a9d70e1 100644
--- a/progress/tty.go
+++ b/progress/tty.go
@@ -146,12 +146,19 @@ func lineText(event Event, terminalWidth, statusPadding int, color bool) string
 	if padding < 0 {
 		padding = 0
 	}
+	// calculate the max length for the status text, on errors it
+	// is 2-3 lines long and breaks the line formating
+	maxStatusLen := terminalWidth - textLen - statusPadding - 15
+	status := event.StatusText
+	if len(status) > maxStatusLen {
+		status = status[:maxStatusLen] + "..."
+	}
 	text := fmt.Sprintf(" %s %s %s%s %s",
 		event.spinner.String(),
 		event.ID,
 		event.Text,
 		strings.Repeat(" ", padding),
-		event.StatusText,
+		status,
 	)
 	timer := fmt.Sprintf("%.1fs\n", elapsed)
 	o := align(text, timer, terminalWidth)


Test patch:
diff --git a/ecs/cloudformation_test.go b/ecs/cloudformation_test.go
index b4584a04..850d2a7e 100644
--- a/ecs/cloudformation_test.go
+++ b/ecs/cloudformation_test.go
@@ -365,7 +365,7 @@ volumes:
     external: true
     name: fs-123abc
 `, useDefaultVPC, func(m *MockAPIMockRecorder) {
-		m.ResolveFileSystem(gomock.Any(), "fs-123abc").Return(existingAWSResource{id: "fs-123abc"}, nil)
+		m.FileSystemExists(gomock.Any(), "fs-123abc").Return(true, nil)
 	})
 	s := template.Resources["DbdataNFSMountTargetOnSubnet1"].(*efs.MountTarget)
 	assert.Check(t, s != nil)
@@ -393,7 +393,7 @@ volumes:
 		m.FindFileSystem(gomock.Any(), map[string]string{
 			compose.ProjectTag: t.Name(),
 			compose.VolumeTag:  "db-data",
-		}).Return(nil, nil)
+		}).Return("", nil)
 	})
 	n := volumeResourceName("db-data")
 	f := template.Resources[n].(*efs.FileSystem)
@@ -409,25 +409,6 @@ volumes:
 	assert.Equal(t, s.FileSystemId, cloudformation.Ref(n)) //nolint:staticcheck
 }
 
-func TestCreateAccessPoint(t *testing.T) {
-	template := convertYaml(t, `
-services:
-  test:
-    image: nginx
-volumes:
-  db-data:
-    driver_opts:
-      uid: 1002
-      gid: 1002
-`, useDefaultVPC, func(m *MockAPIMockRecorder) {
-		m.FindFileSystem(gomock.Any(), gomock.Any()).Return(nil, nil)
-	})
-	a := template.Resources["DbdataAccessPoint"].(*efs.AccessPoint)
-	assert.Check(t, a != nil)
-	assert.Equal(t, a.PosixUser.Uid, "1002") //nolint:staticcheck
-	assert.Equal(t, a.PosixUser.Gid, "1002") //nolint:staticcheck
-}
-
 func TestReusePreviousVolume(t *testing.T) {
 	template := convertYaml(t, `
 services:
@@ -439,7 +420,7 @@ volumes:
 		m.FindFileSystem(gomock.Any(), map[string]string{
 			compose.ProjectTag: t.Name(),
 			compose.VolumeTag:  "db-data",
-		}).Return(existingAWSResource{id: "fs-123abc"}, nil)
+		}).Return("fs-123abc", nil)
 	})
 	s := template.Resources["DbdataNFSMountTargetOnSubnet1"].(*efs.MountTarget)
 	assert.Check(t, s != nil)
@@ -516,10 +497,7 @@ services:
   test:
     image: nginx
 `, useDefaultVPC, func(m *MockAPIMockRecorder) {
-		m.ResolveCluster(gomock.Any(), "arn:aws:ecs:region:account:cluster/name").Return(existingAWSResource{
-			arn: "arn:aws:ecs:region:account:cluster/name",
-			id:  "name",
-		}, nil)
+		m.ClusterExists(gomock.Any(), "arn:aws:ecs:region:account:cluster/name").Return(true, nil)
 	})
 	assert.Equal(t, template.Metadata["Cluster"], "arn:aws:ecs:region:account:cluster/name")
 }
@@ -568,10 +546,7 @@ func getMainContainer(def *ecs.TaskDefinition, t *testing.T) ecs.TaskDefinition_
 
 func useDefaultVPC(m *MockAPIMockRecorder) {
 	m.GetDefaultVPC(gomock.Any()).Return("vpc-123", nil)
-	m.GetSubNets(gomock.Any(), "vpc-123").Return([]awsResource{
-		existingAWSResource{id: "subnet1"},
-		existingAWSResource{id: "subnet2"},
-	}, nil)
+	m.GetSubNets(gomock.Any(), "vpc-123").Return([]string{"subnet1", "subnet2"}, nil)
 }
 
 func useGPU(m *MockAPIMockRecorder) {
diff --git a/ecs/testdata/simple/simple-cloudformation-conversion.golden b/ecs/testdata/simple/simple-cloudformation-conversion.golden
index 51812113..7e29555a 100644
--- a/ecs/testdata/simple/simple-cloudformation-conversion.golden
+++ b/ecs/testdata/simple/simple-cloudformation-conversion.golden
@@ -98,10 +98,7 @@
       ],
       "Properties": {
         "Cluster": {
-          "Fn::GetAtt": [
-            "Cluster",
-            "Arn"
-          ]
+          "Ref": "Cluster"
         },
         "DeploymentConfiguration": {
           "MaximumPercent": 200,
@@ -302,7 +299,6 @@
               "Action": [
                 "sts:AssumeRole"
               ],
-              "Condition": {},
               "Effect": "Allow",
               "Principal": {
                 "Service": "ecs-tasks.amazonaws.com"