diff --git a/internal/pkg/deploy/cloudformation/stack/env.go b/internal/pkg/deploy/cloudformation/stack/env.go index abce4a26051..14074577316 100644 --- a/internal/pkg/deploy/cloudformation/stack/env.go +++ b/internal/pkg/deploy/cloudformation/stack/env.go @@ -5,13 +5,10 @@ package stack import ( "fmt" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/cloudformation" - "github.com/aws/copilot-cli/internal/pkg/manifest" - "gopkg.in/yaml.v3" - "strings" - "github.com/aws/copilot-cli/internal/pkg/config" "github.com/aws/copilot-cli/internal/pkg/deploy" "github.com/aws/copilot-cli/internal/pkg/template" @@ -90,11 +87,6 @@ func (e *EnvStackConfig) Template() (string, error) { if err != nil { return "", err } - - securityGroupConfig, err := getSecurityGroupConfig(e.in.Mft) - if err != nil { - return "", err - } forceUpdateID := e.lastForceUpdateID if e.in.ForceUpdate { id, err := uuid.NewRandom() @@ -115,7 +107,6 @@ func (e *EnvStackConfig) Template() (string, error) { CustomInternalALBSubnets: e.internalALBSubnets(), AllowVPCIngress: aws.BoolValue(e.in.Mft.HTTPConfig.Private.SecurityGroupsConfig.Ingress.VPCIngress), Telemetry: e.telemetryConfig(), - SecurityGroupConfig: securityGroupConfig, CDNConfig: e.cdnConfig(), Version: e.in.Version, @@ -129,30 +120,6 @@ func (e *EnvStackConfig) Template() (string, error) { return content.String(), nil } -func getSecurityGroupConfig(mft *manifest.Environment) (*template.SecurityGroupConfig, error) { - var ingress string - if mft != nil && !mft.Network.VPC.SecurityGroupConfig.Ingress.IsZero() { - out, err := yaml.Marshal(mft.Network.VPC.SecurityGroupConfig.Ingress) - if err != nil { - return &template.SecurityGroupConfig{}, fmt.Errorf("marshal security group ingress from environment manifest to embed in template: %w", err) - } - ingress = strings.TrimSpace(string(out)) - } - - var egress string - if mft != nil && !mft.Network.VPC.SecurityGroupConfig.Egress.IsZero() { - out, err := yaml.Marshal(mft.Network.VPC.SecurityGroupConfig.Egress) - if err != nil { - return &template.SecurityGroupConfig{}, fmt.Errorf("marshal security group egress from environment manifest to embed in template: %w", err) - } - egress = strings.TrimSpace(string(out)) - } - return &template.SecurityGroupConfig{ - Ingress: ingress, - Egress: egress, - }, nil -} - // Parameters returns the parameters to be passed into an environment CloudFormation template. func (e *EnvStackConfig) Parameters() ([]*cloudformation.Parameter, error) { httpsListener := "false" diff --git a/internal/pkg/deploy/cloudformation/stack/env_integration_test.go b/internal/pkg/deploy/cloudformation/stack/env_integration_test.go index 0be2556230e..3ad3ada4c85 100644 --- a/internal/pkg/deploy/cloudformation/stack/env_integration_test.go +++ b/internal/pkg/deploy/cloudformation/stack/env_integration_test.go @@ -64,59 +64,6 @@ observability: }(), wantedFileName: "template-with-imported-certs-observability.yml", }, - - "generate template with embedded manifest file with custom security groups rules added by the customer": { - input: func() *deploy.CreateEnvironmentInput { - rawMft := `name: test -type: Environment -# Create the public ALB with certificates attached. -http: - public: - certificates: - - cert-1 - - cert-2 -observability: - container_insights: true # Enable container insights. -network: - vpc: - security_group: - ingress: - - IpProtocol: tcp - FromPort: 0 - ToPort: 65535 - - IpProtocol: tcp - FromPort: 1 - ToPort: 6 - egress: - - IpProtocol: tcp - FromPort: 0 - ToPort: 65535` - var mft manifest.Environment - err := yaml.Unmarshal([]byte(rawMft), &mft) - require.NoError(t, err) - return &deploy.CreateEnvironmentInput{ - Version: "1.x", - App: deploy.AppInformation{ - AccountPrincipalARN: "arn:aws:iam::000000000:root", - Name: "demo", - }, - Name: "test", - ArtifactBucketARN: "arn:aws:s3:::mockbucket", - ArtifactBucketKeyARN: "arn:aws:kms:us-west-2:000000000:key/1234abcd-12ab-34cd-56ef-1234567890ab", - CustomResourcesURLs: map[string]string{ - "CertificateValidationFunction": "https://mockbucket.s3-us-west-2.amazonaws.com/dns-cert-validator", - "DNSDelegationFunction": "https://mockbucket.s3-us-west-2.amazonaws.com/dns-delegation", - "CustomDomainFunction": "https://mockbucket.s3-us-west-2.amazonaws.com/custom-domain", - }, - AllowVPCIngress: true, - Mft: &mft, - RawMft: []byte(rawMft), - } - }(), - - wantedFileName: "template-with-custom-security-group.yml", - }, - "generate template with custom resources": { input: func() *deploy.CreateEnvironmentInput { rawMft := `name: test diff --git a/internal/pkg/deploy/cloudformation/stack/env_test.go b/internal/pkg/deploy/cloudformation/stack/env_test.go index f68a0eaded2..195e33a5924 100644 --- a/internal/pkg/deploy/cloudformation/stack/env_test.go +++ b/internal/pkg/deploy/cloudformation/stack/env_test.go @@ -56,7 +56,6 @@ func TestEnv_Template(t *testing.T) { Key: "mockkey4", }, }, - SecurityGroupConfig: &template.SecurityGroupConfig{}, Telemetry: &template.Telemetry{ EnableContainerInsights: false, }, diff --git a/internal/pkg/deploy/cloudformation/stack/testdata/environments/template-with-custom-security-group.yml b/internal/pkg/deploy/cloudformation/stack/testdata/environments/template-with-custom-security-group.yml deleted file mode 100644 index 6348910e056..00000000000 --- a/internal/pkg/deploy/cloudformation/stack/testdata/environments/template-with-custom-security-group.yml +++ /dev/null @@ -1,1009 +0,0 @@ -# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: MIT-0 -Description: CloudFormation environment template for infrastructure shared among Copilot workloads. -Metadata: - Manifest: | - name: test - type: Environment - # Create the public ALB with certificates attached. - http: - public: - certificates: - - cert-1 - - cert-2 - observability: - container_insights: true # Enable container insights. - network: - vpc: - security_group: - ingress: - - IpProtocol: tcp - FromPort: 0 - ToPort: 65535 - - IpProtocol: tcp - FromPort: 1 - ToPort: 6 - egress: - - IpProtocol: tcp - FromPort: 0 - ToPort: 65535 -Parameters: - AppName: - Type: String - EnvironmentName: - Type: String - ALBWorkloads: - Type: String - InternalALBWorkloads: - Type: String - EFSWorkloads: - Type: String - NATWorkloads: - Type: String - ToolsAccountPrincipalARN: - Type: String - AppDNSName: - Type: String - AppDNSDelegationRole: - Type: String - Aliases: - Type: String - CreateHTTPSListener: - Type: String - AllowedValues: [true, false] - CreateInternalHTTPSListener: - Type: String - AllowedValues: [true, false] - ServiceDiscoveryEndpoint: - Type: String -Conditions: - CreateALB: - !Not [!Equals [ !Ref ALBWorkloads, "" ]] - DelegateDNS: - !Not [!Equals [ !Ref AppDNSName, "" ]] - ExportHTTPSListener: !And - - !Condition CreateALB - - !Equals [!Ref CreateHTTPSListener, true] - ExportInternalHTTPSListener: !And - - !Condition CreateInternalALB - - !Equals [ !Ref CreateInternalHTTPSListener, true] - CreateEFS: - !Not [!Equals [ !Ref EFSWorkloads, ""]] - CreateInternalALB: - !Not [!Equals [ !Ref InternalALBWorkloads, ""]] - CreateNATGateways: - !Not [!Equals [ !Ref NATWorkloads, ""]] - ManagedAliases: !And - - !Condition DelegateDNS - - !Not [!Equals [ !Ref Aliases, "" ]] -Resources: - VPC: - Metadata: - 'aws:copilot:description': 'A Virtual Private Cloud to control networking of your AWS resources' - Type: AWS::EC2::VPC - Properties: - CidrBlock: 10.0.0.0/16 - EnableDnsHostnames: true - EnableDnsSupport: true - InstanceTenancy: default - Tags: - - Key: Name - Value: !Sub 'copilot-${AppName}-${EnvironmentName}' - - PublicRouteTable: - Metadata: - 'aws:copilot:description': "A custom route table that directs network traffic for the public subnets" - Type: AWS::EC2::RouteTable - Properties: - VpcId: !Ref VPC - Tags: - - Key: Name - Value: !Sub 'copilot-${AppName}-${EnvironmentName}' - - DefaultPublicRoute: - Type: AWS::EC2::Route - DependsOn: InternetGatewayAttachment - Properties: - RouteTableId: !Ref PublicRouteTable - DestinationCidrBlock: 0.0.0.0/0 - GatewayId: !Ref InternetGateway - - InternetGateway: - Metadata: - 'aws:copilot:description': 'An Internet Gateway to connect to the public internet' - Type: AWS::EC2::InternetGateway - Properties: - Tags: - - Key: Name - Value: !Sub 'copilot-${AppName}-${EnvironmentName}' - - InternetGatewayAttachment: - Type: AWS::EC2::VPCGatewayAttachment - Properties: - InternetGatewayId: !Ref InternetGateway - VpcId: !Ref VPC - PublicSubnet1: - Metadata: - 'aws:copilot:description': 'Public subnet 1 for resources that can access the internet' - Type: AWS::EC2::Subnet - Properties: - CidrBlock: 10.0.0.0/24 - VpcId: !Ref VPC - AvailabilityZone: !Select [ 0, !GetAZs '' ] - MapPublicIpOnLaunch: true - Tags: - - Key: Name - Value: !Sub 'copilot-${AppName}-${EnvironmentName}-pub0' - PublicSubnet2: - Metadata: - 'aws:copilot:description': 'Public subnet 2 for resources that can access the internet' - Type: AWS::EC2::Subnet - Properties: - CidrBlock: 10.0.1.0/24 - VpcId: !Ref VPC - AvailabilityZone: !Select [ 1, !GetAZs '' ] - MapPublicIpOnLaunch: true - Tags: - - Key: Name - Value: !Sub 'copilot-${AppName}-${EnvironmentName}-pub1' - PrivateSubnet1: - Metadata: - 'aws:copilot:description': 'Private subnet 1 for resources with no internet access' - Type: AWS::EC2::Subnet - Properties: - CidrBlock: 10.0.2.0/24 - VpcId: !Ref VPC - AvailabilityZone: !Select [ 0, !GetAZs '' ] - MapPublicIpOnLaunch: false - Tags: - - Key: Name - Value: !Sub 'copilot-${AppName}-${EnvironmentName}-priv0' - PrivateSubnet2: - Metadata: - 'aws:copilot:description': 'Private subnet 2 for resources with no internet access' - Type: AWS::EC2::Subnet - Properties: - CidrBlock: 10.0.3.0/24 - VpcId: !Ref VPC - AvailabilityZone: !Select [ 1, !GetAZs '' ] - MapPublicIpOnLaunch: false - Tags: - - Key: Name - Value: !Sub 'copilot-${AppName}-${EnvironmentName}-priv1' - PublicSubnet1RouteTableAssociation: - Type: AWS::EC2::SubnetRouteTableAssociation - Properties: - RouteTableId: !Ref PublicRouteTable - SubnetId: !Ref PublicSubnet1 - PublicSubnet2RouteTableAssociation: - Type: AWS::EC2::SubnetRouteTableAssociation - Properties: - RouteTableId: !Ref PublicRouteTable - SubnetId: !Ref PublicSubnet2 - - NatGateway1Attachment: - Type: AWS::EC2::EIP - Condition: CreateNATGateways - DependsOn: InternetGatewayAttachment - Properties: - Domain: vpc - NatGateway1: - Metadata: - 'aws:copilot:description': 'NAT Gateway 1 enabling workloads placed in private subnet 1 to reach the internet' - Type: AWS::EC2::NatGateway - Condition: CreateNATGateways - Properties: - AllocationId: !GetAtt NatGateway1Attachment.AllocationId - SubnetId: !Ref PublicSubnet1 - Tags: - - Key: Name - Value: !Sub 'copilot-${AppName}-${EnvironmentName}-0' - PrivateRouteTable1: - Type: AWS::EC2::RouteTable - Condition: CreateNATGateways - Properties: - VpcId: !Ref 'VPC' - PrivateRoute1: - Type: AWS::EC2::Route - Condition: CreateNATGateways - Properties: - RouteTableId: !Ref PrivateRouteTable1 - DestinationCidrBlock: 0.0.0.0/0 - NatGatewayId: !Ref NatGateway1 - PrivateRouteTable1Association: - Type: AWS::EC2::SubnetRouteTableAssociation - Condition: CreateNATGateways - Properties: - RouteTableId: !Ref PrivateRouteTable1 - SubnetId: !Ref PrivateSubnet1 - NatGateway2Attachment: - Type: AWS::EC2::EIP - Condition: CreateNATGateways - DependsOn: InternetGatewayAttachment - Properties: - Domain: vpc - NatGateway2: - Metadata: - 'aws:copilot:description': 'NAT Gateway 2 enabling workloads placed in private subnet 2 to reach the internet' - Type: AWS::EC2::NatGateway - Condition: CreateNATGateways - Properties: - AllocationId: !GetAtt NatGateway2Attachment.AllocationId - SubnetId: !Ref PublicSubnet2 - Tags: - - Key: Name - Value: !Sub 'copilot-${AppName}-${EnvironmentName}-1' - PrivateRouteTable2: - Type: AWS::EC2::RouteTable - Condition: CreateNATGateways - Properties: - VpcId: !Ref 'VPC' - PrivateRoute2: - Type: AWS::EC2::Route - Condition: CreateNATGateways - Properties: - RouteTableId: !Ref PrivateRouteTable2 - DestinationCidrBlock: 0.0.0.0/0 - NatGatewayId: !Ref NatGateway2 - PrivateRouteTable2Association: - Type: AWS::EC2::SubnetRouteTableAssociation - Condition: CreateNATGateways - Properties: - RouteTableId: !Ref PrivateRouteTable2 - SubnetId: !Ref PrivateSubnet2 - # Creates a service discovery namespace with the form provided in the parameter. - # For new environments after 1.5.0, this is "env.app.local". For upgraded environments from - # before 1.5.0, this is app.local. - ServiceDiscoveryNamespace: - Metadata: - 'aws:copilot:description': 'A private DNS namespace for discovering services within the environment' - Type: AWS::ServiceDiscovery::PrivateDnsNamespace - Properties: - Name: !Ref ServiceDiscoveryEndpoint - Vpc: !Ref VPC - Cluster: - Metadata: - 'aws:copilot:description': 'An ECS cluster to group your services' - Type: AWS::ECS::Cluster - Properties: - CapacityProviders: ['FARGATE', 'FARGATE_SPOT'] - Configuration: - ExecuteCommandConfiguration: - Logging: DEFAULT - ClusterSettings: - - Name: containerInsights - Value: enabled - PublicLoadBalancerSecurityGroup: - Metadata: - 'aws:copilot:description': 'A security group for your load balancer allowing HTTP and HTTPS traffic' - Condition: CreateALB - Type: AWS::EC2::SecurityGroup - Properties: - GroupDescription: Access to the public facing load balancer - SecurityGroupIngress: - - CidrIp: 0.0.0.0/0 - Description: Allow from anyone on port 80 - FromPort: 80 - IpProtocol: tcp - ToPort: 80 - - CidrIp: 0.0.0.0/0 - Description: Allow from anyone on port 443 - FromPort: 443 - IpProtocol: tcp - ToPort: 443 - VpcId: !Ref VPC - Tags: - - Key: Name - Value: !Sub 'copilot-${AppName}-${EnvironmentName}-lb' - InternalLoadBalancerSecurityGroup: - Metadata: - 'aws:copilot:description': 'A security group for your internal load balancer allowing HTTP traffic from within the VPC' - Condition: CreateInternalALB - Type: AWS::EC2::SecurityGroup - Properties: - GroupDescription: Access to the internal load balancer - VpcId: !Ref VPC - Tags: - - Key: Name - Value: !Sub 'copilot-${AppName}-${EnvironmentName}-internal-lb' - # Only accept requests coming from the public ALB or other containers in the same security group. - EnvironmentSecurityGroup: - Metadata: - 'aws:copilot:description': 'A security group to allow your containers to talk to each other' - Type: AWS::EC2::SecurityGroup - Properties: - GroupDescription: !Join ['', [!Ref AppName, '-', !Ref EnvironmentName, EnvironmentSecurityGroup]] - VpcId: !Ref VPC - Tags: - - Key: Name - Value: !Sub 'copilot-${AppName}-${EnvironmentName}-env' - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: 0 - ToPort: 65535 - - IpProtocol: tcp - FromPort: 1 - ToPort: 6 - SecurityGroupEgress: - - IpProtocol: tcp - FromPort: 0 - ToPort: 65535 - EnvironmentSecurityGroupIngressFromPublicALB: - Type: AWS::EC2::SecurityGroupIngress - Condition: CreateALB - Properties: - Description: Ingress from the public ALB - GroupId: !Ref EnvironmentSecurityGroup - IpProtocol: -1 - SourceSecurityGroupId: !Ref PublicLoadBalancerSecurityGroup - EnvironmentSecurityGroupIngressFromInternalALB: - Type: AWS::EC2::SecurityGroupIngress - Condition: CreateInternalALB - Properties: - Description: Ingress from the internal ALB - GroupId: !Ref EnvironmentSecurityGroup - IpProtocol: -1 - SourceSecurityGroupId: !Ref InternalLoadBalancerSecurityGroup - EnvironmentSecurityGroupIngressFromSelf: - Type: AWS::EC2::SecurityGroupIngress - Properties: - Description: Ingress from other containers in the same security group - GroupId: !Ref EnvironmentSecurityGroup - IpProtocol: -1 - SourceSecurityGroupId: !Ref EnvironmentSecurityGroup - InternalALBIngressFromEnvironmentSecurityGroup: - Type: AWS::EC2::SecurityGroupIngress - Condition: CreateInternalALB - Properties: - Description: Ingress from the env security group - GroupId: !Ref InternalLoadBalancerSecurityGroup - IpProtocol: -1 - SourceSecurityGroupId: !Ref EnvironmentSecurityGroup - PublicLoadBalancer: - Metadata: - 'aws:copilot:description': 'An Application Load Balancer to distribute public traffic to your services' - Condition: CreateALB - Type: AWS::ElasticLoadBalancingV2::LoadBalancer - Properties: - Scheme: internet-facing - SecurityGroups: [ !GetAtt PublicLoadBalancerSecurityGroup.GroupId ] - Subnets: [ !Ref PublicSubnet1, !Ref PublicSubnet2, ] - Type: application - # Assign a dummy target group that with no real services as targets, so that we can create - # the listeners for the services. - DefaultHTTPTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Condition: CreateALB - Properties: - # Check if your application is healthy within 20 = 10*2 seconds, compared to 2.5 mins = 30*5 seconds. - HealthCheckIntervalSeconds: 10 # Default is 30. - HealthyThresholdCount: 2 # Default is 5. - HealthCheckTimeoutSeconds: 5 - Port: 80 - Protocol: HTTP - TargetGroupAttributes: - - Key: deregistration_delay.timeout_seconds - Value: 60 # Default is 300. - TargetType: ip - VpcId: !Ref VPC - HTTPListener: - Metadata: - 'aws:copilot:description': 'A load balancer listener to route HTTP traffic' - Type: AWS::ElasticLoadBalancingV2::Listener - Condition: CreateALB - Properties: - DefaultActions: - - TargetGroupArn: !Ref DefaultHTTPTargetGroup - Type: forward - LoadBalancerArn: !Ref PublicLoadBalancer - Port: 80 - Protocol: HTTP - HTTPSListener: - Metadata: - 'aws:copilot:description': 'A load balancer listener to route HTTPS traffic' - Type: AWS::ElasticLoadBalancingV2::Listener - Condition: ExportHTTPSListener - Properties: - Certificates: - - CertificateArn: cert-1 - DefaultActions: - - TargetGroupArn: !Ref DefaultHTTPTargetGroup - Type: forward - LoadBalancerArn: !Ref PublicLoadBalancer - Port: 443 - Protocol: HTTPS - HTTPSImportCertificate2: - Type: AWS::ElasticLoadBalancingV2::ListenerCertificate - Condition: ExportHTTPSListener - Properties: - ListenerArn: !Ref HTTPSListener - Certificates: - - CertificateArn: cert-2 - InternalLoadBalancer: - Metadata: - 'aws:copilot:description': 'An internal Application Load Balancer to distribute private traffic from within the VPC to your services' - Condition: CreateInternalALB - Type: AWS::ElasticLoadBalancingV2::LoadBalancer - Properties: - Scheme: internal - SecurityGroups: [ !GetAtt InternalLoadBalancerSecurityGroup.GroupId ] - Subnets: [ !Ref PrivateSubnet1, !Ref PrivateSubnet2, ] - Type: application - DefaultInternalHTTPTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Condition: CreateInternalALB - Properties: - # Check if your application is healthy within 20 = 10*2 seconds, compared to 2.5 mins = 30*5 seconds. - HealthCheckIntervalSeconds: 10 # Default is 30. - HealthyThresholdCount: 2 # Default is 5. - HealthCheckTimeoutSeconds: 5 - Port: 80 - Protocol: HTTP - TargetGroupAttributes: - - Key: deregistration_delay.timeout_seconds - Value: 60 # Default is 300. - TargetType: ip - VpcId: !Ref VPC - InternalHTTPListener: - Metadata: - 'aws:copilot:description': 'An internal load balancer listener to route HTTP traffic' - Type: AWS::ElasticLoadBalancingV2::Listener - Condition: CreateInternalALB - Properties: - DefaultActions: - - TargetGroupArn: !Ref DefaultInternalHTTPTargetGroup - Type: forward - LoadBalancerArn: !Ref InternalLoadBalancer - Port: 80 - Protocol: HTTP - InternalHTTPSListener: - Metadata: - 'aws:copilot:description': 'An internal load balancer listener to route HTTPS traffic' - Type: AWS::ElasticLoadBalancingV2::Listener - Condition: ExportInternalHTTPSListener - Properties: - DefaultActions: - - TargetGroupArn: !Ref DefaultInternalHTTPTargetGroup - Type: forward - LoadBalancerArn: !Ref InternalLoadBalancer - Port: 443 - Protocol: HTTPS - InternalWorkloadsHostedZone: - Metadata: - 'aws:copilot:description': 'A hosted zone named test.demo.internal for backends behind a private load balancer' - Condition: CreateInternalALB - Type: AWS::Route53::HostedZone - Properties: - Name: !Sub ${EnvironmentName}.${AppName}.internal - VPCs: - - VPCId: !Ref VPC - VPCRegion: !Ref AWS::Region - FileSystem: - Condition: CreateEFS - Type: AWS::EFS::FileSystem - Metadata: - 'aws:copilot:description': 'An EFS filesystem for persistent task storage' - Properties: - BackupPolicy: - Status: ENABLED - Encrypted: true - FileSystemPolicy: - Version: '2012-10-17' - Id: CopilotEFSPolicy - Statement: - - Sid: AllowIAMFromTaggedRoles - Effect: Allow - Principal: - AWS: '*' - Action: - - elasticfilesystem:ClientWrite - - elasticfilesystem:ClientMount - Condition: - Bool: - 'elasticfilesystem:AccessedViaMountTarget': true - StringEquals: - 'iam:ResourceTag/copilot-application': !Sub '${AppName}' - 'iam:ResourceTag/copilot-environment': !Sub '${EnvironmentName}' - - Sid: DenyUnencryptedAccess - Effect: Deny - Principal: '*' - Action: 'elasticfilesystem:*' - Condition: - Bool: - 'aws:SecureTransport': false - LifecyclePolicies: - - TransitionToIA: AFTER_30_DAYS - PerformanceMode: generalPurpose - ThroughputMode: bursting - EFSSecurityGroup: - Metadata: - 'aws:copilot:description': 'A security group to allow your containers to talk to EFS storage' - Type: AWS::EC2::SecurityGroup - Condition: CreateEFS - Properties: - GroupDescription: !Join ['', [!Ref AppName, '-', !Ref EnvironmentName, EFSSecurityGroup]] - VpcId: !Ref VPC - Tags: - - Key: Name - Value: !Sub 'copilot-${AppName}-${EnvironmentName}-efs' - EFSSecurityGroupIngressFromEnvironment: - Type: AWS::EC2::SecurityGroupIngress - Condition: CreateEFS - Properties: - Description: Ingress from containers in the Environment Security Group. - GroupId: !Ref EFSSecurityGroup - IpProtocol: -1 - SourceSecurityGroupId: !Ref EnvironmentSecurityGroup - MountTarget1: - Type: AWS::EFS::MountTarget - Condition: CreateEFS - Properties: - FileSystemId: !Ref FileSystem - SubnetId: !Ref PrivateSubnet1 - SecurityGroups: - - !Ref EFSSecurityGroup - MountTarget2: - Type: AWS::EFS::MountTarget - Condition: CreateEFS - Properties: - FileSystemId: !Ref FileSystem - SubnetId: !Ref PrivateSubnet2 - SecurityGroups: - - !Ref EFSSecurityGroup - # The CloudformationExecutionRole definition must be immediately followed with DeletionPolicy: Retain. - # See #1533. - CloudformationExecutionRole: - Metadata: - 'aws:copilot:description': 'An IAM Role for AWS CloudFormation to manage resources' - DeletionPolicy: Retain - Type: AWS::IAM::Role - Properties: - RoleName: !Sub ${AWS::StackName}-CFNExecutionRole - AssumeRolePolicyDocument: - Version: '2012-10-17' - Statement: - - Effect: Allow - Principal: - Service: - - 'cloudformation.amazonaws.com' - - 'lambda.amazonaws.com' - Action: sts:AssumeRole - Path: / - Policies: - - PolicyName: executeCfn - # This policy is more permissive than the managed PowerUserAccess - # since it allows arbitrary role creation, which is needed for the - # ECS task role specified by the customers. - PolicyDocument: - Version: '2012-10-17' - Statement: - - - Effect: Allow - NotAction: - - 'organizations:*' - - 'account:*' - Resource: '*' - - - Effect: Allow - Action: - - 'organizations:DescribeOrganization' - - 'account:ListRegions' - Resource: '*' - - EnvironmentManagerRole: - Metadata: - 'aws:copilot:description': 'An IAM Role to describe resources in your environment' - DeletionPolicy: Retain - Type: AWS::IAM::Role - DependsOn: CloudformationExecutionRole - Properties: - RoleName: !Sub ${AWS::StackName}-EnvManagerRole - AssumeRolePolicyDocument: - Version: '2012-10-17' - Statement: - - Effect: Allow - Principal: - AWS: !Sub ${ToolsAccountPrincipalARN} - Action: sts:AssumeRole - Path: / - Policies: - - PolicyName: root - PolicyDocument: - Version: '2012-10-17' - Statement: - - Sid: ImportedCertificates - Effect: Allow - Action: [ - acm:DescribeCertificate - ] - Resource: - - "cert-1" - - "cert-2" - - Sid: CloudwatchLogs - Effect: Allow - Action: [ - "logs:GetLogRecord", - "logs:GetQueryResults", - "logs:StartQuery", - "logs:GetLogEvents", - "logs:DescribeLogStreams", - "logs:StopQuery", - "logs:TestMetricFilter", - "logs:FilterLogEvents", - "logs:GetLogGroupFields", - "logs:GetLogDelivery" - ] - Resource: "*" - - Sid: Cloudwatch - Effect: Allow - Action: [ - "cloudwatch:DescribeAlarms" - ] - Resource: "*" - - Sid: ECS - Effect: Allow - Action: [ - "ecs:ListAttributes", - "ecs:ListTasks", - "ecs:DescribeServices", - "ecs:DescribeTaskSets", - "ecs:ListContainerInstances", - "ecs:DescribeContainerInstances", - "ecs:DescribeTasks", - "ecs:DescribeClusters", - "ecs:UpdateService", - "ecs:PutAttributes", - "ecs:StartTelemetrySession", - "ecs:StartTask", - "ecs:StopTask", - "ecs:ListServices", - "ecs:ListTaskDefinitionFamilies", - "ecs:DescribeTaskDefinition", - "ecs:ListTaskDefinitions", - "ecs:ListClusters", - "ecs:RunTask" - ] - Resource: "*" - - Sid: ExecuteCommand - Effect: Allow - Action: [ - "ecs:ExecuteCommand" - ] - Resource: "*" - Condition: - StringEquals: - 'aws:ResourceTag/copilot-application': !Sub '${AppName}' - 'aws:ResourceTag/copilot-environment': !Sub '${EnvironmentName}' - - Sid: StartStateMachine - Effect: Allow - Action: - - "states:StartExecution" - Resource: - - !Sub "arn:aws:states:${AWS::Region}:${AWS::AccountId}:stateMachine:${AppName}-${EnvironmentName}-*" - - Sid: CloudFormation - Effect: Allow - Action: [ - "cloudformation:CancelUpdateStack", - "cloudformation:CreateChangeSet", - "cloudformation:CreateStack", - "cloudformation:DeleteChangeSet", - "cloudformation:DeleteStack", - "cloudformation:Describe*", - "cloudformation:DetectStackDrift", - "cloudformation:DetectStackResourceDrift", - "cloudformation:ExecuteChangeSet", - "cloudformation:GetTemplate", - "cloudformation:GetTemplateSummary", - "cloudformation:UpdateStack", - "cloudformation:UpdateTerminationProtection" - ] - Resource: "*" - - Sid: GetAndPassCopilotRoles - Effect: Allow - Action: [ - "iam:GetRole", - "iam:PassRole" - ] - Resource: "*" - Condition: - StringEquals: - 'iam:ResourceTag/copilot-application': !Sub '${AppName}' - 'iam:ResourceTag/copilot-environment': !Sub '${EnvironmentName}' - - Sid: ECR - Effect: Allow - Action: [ - "ecr:BatchGetImage", - "ecr:BatchCheckLayerAvailability", - "ecr:CompleteLayerUpload", - "ecr:DescribeImages", - "ecr:DescribeRepositories", - "ecr:GetDownloadUrlForLayer", - "ecr:InitiateLayerUpload", - "ecr:ListImages", - "ecr:ListTagsForResource", - "ecr:PutImage", - "ecr:UploadLayerPart", - "ecr:GetAuthorizationToken" - ] - Resource: "*" - - Sid: ResourceGroups - Effect: Allow - Action: [ - "resource-groups:GetGroup", - "resource-groups:GetGroupQuery", - "resource-groups:GetTags", - "resource-groups:ListGroupResources", - "resource-groups:ListGroups", - "resource-groups:SearchResources" - ] - Resource: "*" - - Sid: SSM - Effect: Allow - Action: [ - "ssm:DeleteParameter", - "ssm:DeleteParameters", - "ssm:GetParameter", - "ssm:GetParameters", - "ssm:GetParametersByPath" - ] - Resource: "*" - - Sid: SSMSecret - Effect: Allow - Action: [ - "ssm:PutParameter", - "ssm:AddTagsToResource" - ] - Resource: - - !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/copilot/${AppName}/${EnvironmentName}/secrets/*' - - Sid: ELBv2 - Effect: Allow - Action: [ - "elasticloadbalancing:DescribeLoadBalancerAttributes", - "elasticloadbalancing:DescribeSSLPolicies", - "elasticloadbalancing:DescribeLoadBalancers", - "elasticloadbalancing:DescribeTargetGroupAttributes", - "elasticloadbalancing:DescribeListeners", - "elasticloadbalancing:DescribeTags", - "elasticloadbalancing:DescribeTargetHealth", - "elasticloadbalancing:DescribeTargetGroups", - "elasticloadbalancing:DescribeRules" - ] - Resource: "*" - - Sid: BuiltArtifactAccess - Effect: Allow - Action: [ - "s3:ListBucketByTags", - "s3:GetLifecycleConfiguration", - "s3:GetBucketTagging", - "s3:GetInventoryConfiguration", - "s3:GetObjectVersionTagging", - "s3:ListBucketVersions", - "s3:GetBucketLogging", - "s3:ListBucket", - "s3:GetAccelerateConfiguration", - "s3:GetBucketPolicy", - "s3:GetObjectVersionTorrent", - "s3:GetObjectAcl", - "s3:GetEncryptionConfiguration", - "s3:GetBucketRequestPayment", - "s3:GetObjectVersionAcl", - "s3:GetObjectTagging", - "s3:GetMetricsConfiguration", - "s3:HeadBucket", - "s3:GetBucketPublicAccessBlock", - "s3:GetBucketPolicyStatus", - "s3:ListBucketMultipartUploads", - "s3:GetBucketWebsite", - "s3:ListJobs", - "s3:GetBucketVersioning", - "s3:GetBucketAcl", - "s3:GetBucketNotification", - "s3:GetReplicationConfiguration", - "s3:ListMultipartUploadParts", - "s3:GetObject", - "s3:GetObjectTorrent", - "s3:GetAccountPublicAccessBlock", - "s3:ListAllMyBuckets", - "s3:DescribeJob", - "s3:GetBucketCORS", - "s3:GetAnalyticsConfiguration", - "s3:GetObjectVersionForReplication", - "s3:GetBucketLocation", - "s3:GetObjectVersion", - "kms:Decrypt" - ] - Resource: "*" - - Sid: PutObjectsToArtifactBucket - Effect: Allow - Action: - - s3:PutObject - - s3:PutObjectAcl - Resource: - - arn:aws:s3:::mockbucket - - arn:aws:s3:::mockbucket/* - - Sid: EncryptObjectsInArtifactBucket - Effect: Allow - Action: - - kms:GenerateDataKey - Resource: arn:aws:kms:us-west-2:000000000:key/1234abcd-12ab-34cd-56ef-1234567890ab - - Sid: EC2 - Effect: Allow - Action: [ - "ec2:DescribeSubnets", - "ec2:DescribeSecurityGroups", - "ec2:DescribeNetworkInterfaces", - "ec2:DescribeRouteTables" - ] - Resource: "*" - - Sid: AppRunner - Effect: Allow - Action: [ - "apprunner:DescribeService", - "apprunner:ListOperations", - "apprunner:ListServices", - "apprunner:PauseService", - "apprunner:ResumeService", - "apprunner:StartDeployment", - "apprunner:DescribeObservabilityConfiguration" - ] - Resource: "*" - - Sid: Tags - Effect: Allow - Action: [ - "tag:GetResources" - ] - Resource: "*" - - Sid: ApplicationAutoscaling - Effect: Allow - Action: [ - "application-autoscaling:DescribeScalingPolicies" - ] - Resource: "*" - - Sid: DeleteRoles - Effect: Allow - Action: [ - "iam:DeleteRole", - "iam:ListRolePolicies", - "iam:DeleteRolePolicy" - ] - Resource: - - !GetAtt CloudformationExecutionRole.Arn - - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${AWS::StackName}-EnvManagerRole" - - Sid: DeleteEnvStack - Effect: Allow - Action: - - 'cloudformation:DescribeStacks' - - 'cloudformation:DeleteStack' - Resource: - - !Sub 'arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*' - -Outputs: - VpcId: - Value: !Ref VPC - Export: - Name: !Sub ${AWS::StackName}-VpcId - PublicSubnets: - Value: !Join [ ',', [ !Ref PublicSubnet1, !Ref PublicSubnet2, ] ] - Export: - Name: !Sub ${AWS::StackName}-PublicSubnets - PrivateSubnets: - Value: !Join [ ',', [ !Ref PrivateSubnet1, !Ref PrivateSubnet2, ] ] - Export: - Name: !Sub ${AWS::StackName}-PrivateSubnets - InternetGatewayID: - Value: !Ref InternetGateway - Export: - Name: !Sub ${AWS::StackName}-InternetGatewayID - PublicRouteTableID: - Value: !Ref PublicRouteTable - Export: - Name: !Sub ${AWS::StackName}-PublicRouteTableID - PrivateRouteTableIDs: - Condition: CreateNATGateways - Value: !Join [ ',', [ !Ref PrivateRouteTable1, !Ref PrivateRouteTable2, ] ] - Export: - Name: !Sub ${AWS::StackName}-PrivateRouteTableIDs - ServiceDiscoveryNamespaceID: - Value: !GetAtt ServiceDiscoveryNamespace.Id - Export: - Name: !Sub ${AWS::StackName}-ServiceDiscoveryNamespaceID - EnvironmentSecurityGroup: - Value: !Ref EnvironmentSecurityGroup - Export: - Name: !Sub ${AWS::StackName}-EnvironmentSecurityGroup - PublicLoadBalancerDNSName: - Condition: CreateALB - Value: !GetAtt PublicLoadBalancer.DNSName - Export: - Name: !Sub ${AWS::StackName}-PublicLoadBalancerDNS - PublicLoadBalancerFullName: - Condition: CreateALB - Value: !GetAtt PublicLoadBalancer.LoadBalancerFullName - Export: - Name: !Sub ${AWS::StackName}-PublicLoadBalancerFullName - PublicLoadBalancerHostedZone: - Condition: CreateALB - Value: !GetAtt PublicLoadBalancer.CanonicalHostedZoneID - Export: - Name: !Sub ${AWS::StackName}-CanonicalHostedZoneID - HTTPListenerArn: - Condition: CreateALB - Value: !Ref HTTPListener - Export: - Name: !Sub ${AWS::StackName}-HTTPListenerArn - HTTPSListenerArn: - Condition: ExportHTTPSListener - Value: !Ref HTTPSListener - Export: - Name: !Sub ${AWS::StackName}-HTTPSListenerArn - DefaultHTTPTargetGroupArn: - Condition: CreateALB - Value: !Ref DefaultHTTPTargetGroup - Export: - Name: !Sub ${AWS::StackName}-DefaultHTTPTargetGroup - InternalLoadBalancerDNSName: - Condition: CreateInternalALB - Value: !GetAtt InternalLoadBalancer.DNSName - Export: - Name: !Sub ${AWS::StackName}-InternalLoadBalancerDNS - InternalLoadBalancerFullName: - Condition: CreateInternalALB - Value: !GetAtt InternalLoadBalancer.LoadBalancerFullName - Export: - Name: !Sub ${AWS::StackName}-InternalLoadBalancerFullName - InternalLoadBalancerHostedZone: - Condition: CreateInternalALB - Value: !GetAtt InternalLoadBalancer.CanonicalHostedZoneID - Export: - Name: !Sub ${AWS::StackName}-InternalLoadBalancerCanonicalHostedZoneID - InternalWorkloadsHostedZone: - Condition: CreateInternalALB - Value: !GetAtt InternalWorkloadsHostedZone.Id - Export: - Name: !Sub ${AWS::StackName}-InternalWorkloadsHostedZoneID - InternalWorkloadsHostedZoneName: - Condition: CreateInternalALB - Value: !Sub ${EnvironmentName}.${AppName}.internal - Export: - Name: !Sub ${AWS::StackName}-InternalWorkloadsHostedZoneName - InternalHTTPListenerArn: - Condition: CreateInternalALB - Value: !Ref InternalHTTPListener - Export: - Name: !Sub ${AWS::StackName}-InternalHTTPListenerArn - InternalHTTPSListenerArn: - Condition: ExportInternalHTTPSListener - Value: !Ref InternalHTTPSListener - Export: - Name: !Sub ${AWS::StackName}-InternalHTTPSListenerArn - InternalLoadBalancerSecurityGroup: - Condition: CreateInternalALB - Value: !Ref InternalLoadBalancerSecurityGroup - Export: - Name: !Sub ${AWS::StackName}-InternalLoadBalancerSecurityGroup - ClusterId: - Value: !Ref Cluster - Export: - Name: !Sub ${AWS::StackName}-ClusterId - EnvironmentManagerRoleARN: - Value: !GetAtt EnvironmentManagerRole.Arn - Description: The role to be assumed by the ecs-cli to manage environments. - Export: - Name: !Sub ${AWS::StackName}-EnvironmentManagerRoleARN - CFNExecutionRoleARN: - Value: !GetAtt CloudformationExecutionRole.Arn - Description: The role to be assumed by the Cloudformation service when it deploys application infrastructure. - Export: - Name: !Sub ${AWS::StackName}-CFNExecutionRoleARN - EnabledFeatures: - Value: !Sub '${ALBWorkloads},${InternalALBWorkloads},${EFSWorkloads},${NATWorkloads},${Aliases}' - Description: Required output to force the stack to update if mutating feature params, like ALBWorkloads, does not change the template. - ManagedFileSystemID: - Condition: CreateEFS - Value: !Ref FileSystem - Description: The ID of the Copilot-managed EFS filesystem. - Export: - Name: !Sub ${AWS::StackName}-FilesystemID - LastForceDeployID: - Value: "" - Description: Optionally force the template to update when no immediate resource change is present. \ No newline at end of file diff --git a/internal/pkg/manifest/env.go b/internal/pkg/manifest/env.go index cdb39ea26b5..5f117bca19f 100644 --- a/internal/pkg/manifest/env.go +++ b/internal/pkg/manifest/env.go @@ -95,15 +95,9 @@ type environmentNetworkConfig struct { } type environmentVPCConfig struct { - ID *string `yaml:"id,omitempty"` - CIDR *IPNet `yaml:"cidr,omitempty"` - Subnets subnetsConfiguration `yaml:"subnets,omitempty"` - SecurityGroupConfig securityGroupConfig `yaml:"security_group"` -} - -type securityGroupConfig struct { - Ingress yaml.Node `yaml:"ingress"` - Egress yaml.Node `yaml:"egress"` + ID *string `yaml:"id,omitempty"` + CIDR *IPNet `yaml:"cidr,omitempty"` + Subnets subnetsConfiguration `yaml:"subnets,omitempty"` } type environmentCDNConfig struct { diff --git a/internal/pkg/template/env.go b/internal/pkg/template/env.go index d279b5c0aac..e079a9f22d2 100644 --- a/internal/pkg/template/env.go +++ b/internal/pkg/template/env.go @@ -111,7 +111,6 @@ type EnvOpts struct { CustomInternalALBSubnets []string AllowVPCIngress bool Telemetry *Telemetry - SecurityGroupConfig *SecurityGroupConfig CDNConfig *CDNConfig // If nil, no cdn is to be used @@ -128,12 +127,6 @@ type VPCConfig struct { Managed ManagedVPC } -// SecurityGroupConfig holds the fields to import security group config -type SecurityGroupConfig struct { - Ingress string - Egress string -} - // ImportVPC holds the fields to import VPC resources. type ImportVPC struct { ID string diff --git a/internal/pkg/template/templates/environment/cf.yml b/internal/pkg/template/templates/environment/cf.yml index dbda4372357..98f8439e4ef 100644 --- a/internal/pkg/template/templates/environment/cf.yml +++ b/internal/pkg/template/templates/environment/cf.yml @@ -153,14 +153,6 @@ Resources: Tags: - Key: Name Value: !Sub 'copilot-${AppName}-${EnvironmentName}-env' -{{- if and .SecurityGroupConfig .SecurityGroupConfig.Ingress }} - SecurityGroupIngress: -{{.SecurityGroupConfig.Ingress | indent 8}} -{{- end }} -{{- if and .SecurityGroupConfig .SecurityGroupConfig.Egress }} - SecurityGroupEgress: -{{.SecurityGroupConfig.Egress | indent 8}} -{{- end }} EnvironmentSecurityGroupIngressFromPublicALB: Type: AWS::EC2::SecurityGroupIngress Condition: CreateALB