serverless / serverless

⚡ Serverless Framework – Effortlessly build apps that auto-scale, incur zero costs when idle, and require minimal maintenance using AWS Lambda and other managed cloud services.
https://serverless.com
MIT License
46.43k stars 5.71k forks source link

Access ECR URI Natively #12018

Open felipegenef opened 1 year ago

felipegenef commented 1 year ago

Is there an existing issue for this?

Use case description

Hey guys, I have a project where I created an ECR and a TaskDefinition for AWS FARGATE on my resources tab, but I had to deploy an aws function with my container image to have access to my URI. It would be nice if we could deploy and access ecr uris from within the template... something like ${self:provider.ecr.images.imageName.uri} to get the deployed container URI or simply have imageName as the URI value as default (Similar behaviour from lambda images). This way we would be able to "!Ref" it on the resources tab.

My Code

If you look at it you will see that I use ${ssm:/account-id}.dkr.ecr.${self:provider.region}.amazonaws.com/serverless-${self:service}-${opt:stage,"dev"}:javascriptScriptTaskDefinition instead of a simple ${self:provider.ecr.images.imageName.uri}. I also needed to use the same container as a lambda image to deploy it.

service: fargate-test
frameworkVersion: '3'

provider:
  name: aws
  region: us-east-1
  ecr:
    images:
      javascriptScriptTaskDefinition:
        path: ./javascript
  iamRoleStatements:
    - Effect: Allow
      Action:
        - ecs:RunTask
      Resource: "*"
    - Effect: Allow
      Action:
        - iam:PassRole
      Resource: "*"

functions:
  startTask:
    environment:
      TASK_DEFINITION: !Ref TaskDefinition
      CLUSTER_NAME: !Ref EcsCluster
      SUBNET: !Ref PublicSubnet
      SG: !Ref PublicSecurityGroup
      CONTAINER_NAME: ${self:custom.containerName}
    image:
      name: javascriptScriptTaskDefinition
      command:
        - node
        - index.js
    url: true
    timeout: 70

custom:
  containerCPU: '256'
  containerMemory: '512'
  containerName: javascript-script
  image: ${ssm:/account-id}.dkr.ecr.${self:provider.region}.amazonaws.com/serverless-${self:service}-${opt:stage,"dev"}:javascriptScriptTaskDefinition

resources:
  Resources:
    EcsCluster:
      Type: AWS::ECS::Cluster
      Properties:
        ClusterName: ${self:service}-cluster

    TaskExecutionRole:
      Type: AWS::IAM::Role
      Properties:
        RoleName: ${self:service}-task-execution-role
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                  - ecs-tasks.amazonaws.com
              Action: sts:AssumeRole
        ManagedPolicyArns:
          - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

    TaskDefinition:
      Type: AWS::ECS::TaskDefinition
      Properties:
        Family: ${self:service}-javascript-script-task-definition
        ExecutionRoleArn: !Ref TaskExecutionRole
        NetworkMode: awsvpc
        RequiresCompatibilities:
          - FARGATE
        Cpu: ${self:custom.containerCPU}
        Memory: ${self:custom.containerMemory}
        ContainerDefinitions:
          - Name: ${self:custom.containerName}
            Image: ${self:custom.image}
            Essential: true
            LogConfiguration:
              LogDriver: awslogs
              Options:
                awslogs-group: /ecs/${self:service}-fargate-logs
                awslogs-region: ${self:provider.region}
                awslogs-stream-prefix: fargate-logs
    # CRIA LOGS NO CLOUDWATCH

    ECRLogGroup:
      Type: AWS::Logs::LogGroup
      Properties:
        LogGroupName: /ecs/${self:service}-fargate-logs

    # CRIA A PARTE DE REDES NECESSÁRIA PARA A VPC 

    PublicSecurityGroup:
      Type: AWS::EC2::SecurityGroup
      Properties:
        GroupName: PublicSecurityGroup
        GroupDescription: Public Security Group
        VpcId: !Ref Vpc
        SecurityGroupIngress:
          - IpProtocol: tcp
            FromPort: 0
            ToPort: 65535
            CidrIp: 0.0.0.0/0
        # SecurityGroupEgress:
        #   - IpProtocol: tcp
        #     FromPort: 0
        #     ToPort: 65535
        #     CidrIp: 0.0.0.0/0

    PublicSubnet:
      Type: AWS::EC2::Subnet
      Properties:
        VpcId: !Ref Vpc
        AvailabilityZone: us-east-1a
        CidrBlock: 10.0.1.0/24

    Vpc:
      Type: AWS::EC2::VPC
      Properties:
        CidrBlock: 10.0.0.0/16
        EnableDnsSupport: true
        EnableDnsHostnames: true
        InstanceTenancy: default
        Tags:
          - Key: Name
            Value: ${self:service}-vpc

    InternetGateway:
      Type: AWS::EC2::InternetGateway
      Properties:
        Tags:
          - Key: Name
            Value: ${self:service}-internet-gateway

    InternetGatewayAttachment:
      Type: AWS::EC2::VPCGatewayAttachment
      Properties:
        VpcId: !Ref Vpc
        InternetGatewayId: !Ref InternetGateway

    PublicRouteTable:
      Type: AWS::EC2::RouteTable
      Properties:
        VpcId: !Ref Vpc
        Tags:
          - Key: Name
            Value: ${self:service}-public-route-table

    PublicRoute:
      Type: AWS::EC2::Route
      DependsOn: InternetGatewayAttachment
      Properties:
        RouteTableId: !Ref PublicRouteTable
        DestinationCidrBlock: 0.0.0.0/0
        GatewayId: !Ref InternetGateway

    SubnetRouteTableAssociation:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        SubnetId: !Ref PublicSubnet
        RouteTableId: !Ref PublicRouteTable

Proposed solution (optional)

EX1:

service: fargate-test
frameworkVersion: '3'

provider:
  name: aws
  region: us-east-1
  ecr:
    images:
      imageName:
        path: ./containerFolder
  resources:
    Resources:
      EcsCluster:
        Type: AWS::ECS::Cluster
        Properties:
          ClusterName: ${self:service}-cluster
      TaskDefinition:
        Type: AWS::ECS::TaskDefinition
        Properties:
          RequiresCompatibilities:
            - FARGATE
          Cpu:  250
          Memory: 500
          ContainerDefinitions:
            - Name: container-name
              Image: ${self:custom.ecr.images.imageName.uri}

EX2:

service: fargate-test
frameworkVersion: '3'

provider:
  name: aws
  region: us-east-1
  ecr:
    images:
      imageName:
        path: ./containerFolder
  resources:
    Resources:
      EcsCluster:
        Type: AWS::ECS::Cluster
        Properties:
          ClusterName: ${self:service}-cluster
      TaskDefinition:
        Type: AWS::ECS::TaskDefinition
        Properties:
          RequiresCompatibilities:
            - FARGATE
          Cpu:  250
          Memory: 500
          ContainerDefinitions:
            - Name: container-name
              Image: imageName
medikoo commented 1 year ago

something like ${self:provider.ecr.images.imageName.uri} to get the deployed container URI or simply have imageName as the URI value as default (Similar behaviour from lambda images). This way we would be able to "!Ref" it on the resources tab.

!Ref is for addressing other resources in same CF template, and as this image is not part of a template it's out of question.

Anyway, something that you've proposed below, could work, via e.g. aws variable source extension, e.g. as:

${aws:ecrImage/<imageName>.uri}

For that we can generalize image uri that's currently implemented here: https://github.com/serverless/serverless/blob/17d64e6c94b88a5daf36f28a4fa192c231052cfb/lib/plugins/aws/provider.js#L2348-L2463

And reuse it in context of aws variable resolver: https://github.com/serverless/serverless/blob/17d64e6c94b88a5daf36f28a4fa192c231052cfb/lib/configuration/variables/sources/instance-dependent/get-aws.js

WDYT?

gazzoy commented 1 year ago

I'm interested in this as well.

felipegenef commented 1 year ago

WDYT? ${aws:ecrImage/<imageName>.uri} Looks Awesome ! That would solve a huge problem when you want to deploy fargate images from serverless framework!