serverless-plugin-nested-stacks icon indicating copy to clipboard operation
serverless-plugin-nested-stacks copied to clipboard

Sharing variables/resources between parent and child stacks?

Open kaitlynbrown opened this issue 6 years ago • 4 comments

The documentation doesn't seem to mention anything about this. Is it possible? If so, how?

kaitlynbrown avatar Jul 16 '19 16:07 kaitlynbrown

I have a use case were I need the outputs from one stack into the next one. I'm having a hard time referencing the depended stack since its ID has a random suffix.

Is there a way to accomplish this ? getting the outputs from one nested stack into another ?

gio-trueper avatar Mar 24 '22 20:03 gio-trueper

Seconded @gio-trueper's issue. I am trying to reference an export from a nested stack, but cannot figure out the stack id since it is randomly generated.

abduncan avatar Apr 08 '22 11:04 abduncan

I have figured this out. Below are my serverless.yml and react-infrasctructure.yml (nested stack) files.

I am referencing the CloudFront Distribution Id from the nested stack in the serverless.yml file.

Notice the export of CFDistributionId in react-infrastructure.yml and it's reference in the custom section of the serverless.yml file.

serverless.yml:

service: my-service

provider:
  name: aws
  runtime: nodejs14.x
  stage: dev
  region: us-east-1


custom:

  CFDistributionId: !ImportValue ${self:service}-${self:provider.stage}-CFDistributionId

  webpack:
      webpackConfig: 'webpack.config.js'
      includeModules: true
      packager: 'npm'
      packExternalModulesMaxBuffer: 204800

  nested-stacks:
    location: devops
    stacks:
      - id: reactinfrastructure
        template: react-infrastructure.yml
        timeout: 60
        parameters:
          - ServiceName: ${self:service}
          - EnvironmentStage: ${self:provider.stage}
      # - id: vpc
      #   template: vpc.yml
      #   timeout: 60
      #   parameters:
      #     - ClassB: 10

functions:
  hello:
    handler: apps/portal-api/src/app/index.hello
    description: A function for Hello Application
    events:
      - http:
          path: /hello
          method: get
    environment:
      distributionId: ${self:custom.CFDistributionId}
plugins:
  - serverless-webpack
  - serverless-offline
  - serverless-aws-nested-stacks

react-infrastructure.yml:

AWSTemplateFormatVersion: '2010-09-09'

Description: >
  Creates the infrastructure to host and expose a Single Page Application:
      - An Amazon S3 bucket for hosting the application
      - An Amazon CloudFront distribution to expose the application
      - An Amazon S3 bucket for hosting bucket and cloudfront access logs

Parameters:
  
  ServiceName:
    Description: 'Name of the Serverless service'
    Type: String

  EnvironmentStage:
    Description: The environment (dev, test, prod)
    Type: String
    AllowedValues:
      - dev
      - test
      - prod

Resources:
  # The Amazon S3 bucket into which our Single Page Application build files must be deployed
  S3Bucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketName: !Sub '${ServiceName}-react-app-${EnvironmentStage}'
      PublicAccessBlockConfiguration:
        BlockPublicAcls : true
        BlockPublicPolicy : true
        IgnorePublicAcls : true
        RestrictPublicBuckets : true
      LoggingConfiguration:
        DestinationBucketName: !Ref LoggingBucket
        LogFilePrefix: s3-access-logs
      VersioningConfiguration:
        Status: Enabled
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: 'AES256'

  # The Amazon S3 bucket policy for securing the bucket hosting the application
  BucketPolicy:
    Type: 'AWS::S3::BucketPolicy'
    Properties:
      PolicyDocument:
        Id: MyPolicy
        Version: 2012-10-17
        Statement:
          - Sid: PolicyForCloudFrontPrivateContent
            Effect: Allow
            Principal:
              CanonicalUser: !GetAtt CFOriginAccessIdentity.S3CanonicalUserId
            Action: 's3:GetObject*'
            Resource: !Join 
              - ''
              - - 'arn:aws:s3:::'
                - !Ref S3Bucket
                - /*
      Bucket: !Ref S3Bucket

  # The Amazon S3 bucket into which access logs from S3 (for the application) and CloudFront will be put
  LoggingBucket:
    #checkov:skip=CKV_AWS_18: "This bucket is private and only for storing logs"
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketName: !Sub '${ServiceName}-react-app-${EnvironmentStage}-logs'
      PublicAccessBlockConfiguration:
        BlockPublicAcls : true
        BlockPublicPolicy : true
        IgnorePublicAcls : true
        RestrictPublicBuckets : true
      AccessControl: LogDeliveryWrite
      VersioningConfiguration:
        Status: Enabled
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: 'AES256'
    DeletionPolicy: Delete

  # The Amazon CloudFront distribution exposing our Single Page Application
  CFDistribution:
    #checkov:skip=CKV_AWS_68: "For demo purposes and to reduce cost, no WAF is configured"
    Type: 'AWS::CloudFront::Distribution'
    DependsOn:
      - CFOriginAccessIdentity
    Properties:
      DistributionConfig:
        Origins:
        - DomainName: !GetAtt S3Bucket.RegionalDomainName
          Id: myS3Origin
          S3OriginConfig:
            OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${CFOriginAccessIdentity}"
        Enabled: 'true'
        DefaultRootObject: index.html
        DefaultCacheBehavior:
          AllowedMethods:
          - GET
          - HEAD
          - OPTIONS
          TargetOriginId: myS3Origin
          CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 # CachingOptimized
          OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf # CORS-S3Origin
          ViewerProtocolPolicy: redirect-to-https
        PriceClass: PriceClass_All
        Logging:
          Bucket: !GetAtt LoggingBucket.RegionalDomainName
          Prefix: 'cloudfront-access-logs'

  # The Amazon CloudFront origin access identity
  CFOriginAccessIdentity:
    Type: 'AWS::CloudFront::CloudFrontOriginAccessIdentity'
    DependsOn:
      - S3Bucket
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: !Sub 'access-identity-${ServiceName}-react-app-${EnvironmentStage}'

Outputs:
  ReactBucketName:
    Value: !Sub "${ServiceName}-react-app-${EnvironmentStage}-${CFOriginAccessIdentity}"
  CFDistributionId:
    Value: !Ref CFDistribution
    Export:
      Name: !Sub "${ServiceName}-${EnvironmentStage}-CFDistributionId"

@gio-trueper

abduncan avatar Apr 09 '22 00:04 abduncan

I have figured this out. Below are my serverless.yml and react-infrasctructure.yml (nested stack) files.

I am referencing the CloudFront Distribution Id from the nested stack in the serverless.yml file.

Notice the export of CFDistributionId in react-infrastructure.yml and it's reference in the custom section of the serverless.yml file.

serverless.yml:

service: my-service

provider:
  name: aws
  runtime: nodejs14.x
  stage: dev
  region: us-east-1


custom:

  CFDistributionId: !ImportValue ${self:service}-${self:provider.stage}-CFDistributionId

  webpack:
      webpackConfig: 'webpack.config.js'
      includeModules: true
      packager: 'npm'
      packExternalModulesMaxBuffer: 204800

  nested-stacks:
    location: devops
    stacks:
      - id: reactinfrastructure
        template: react-infrastructure.yml
        timeout: 60
        parameters:
          - ServiceName: ${self:service}
          - EnvironmentStage: ${self:provider.stage}
      # - id: vpc
      #   template: vpc.yml
      #   timeout: 60
      #   parameters:
      #     - ClassB: 10

functions:
  hello:
    handler: apps/portal-api/src/app/index.hello
    description: A function for Hello Application
    events:
      - http:
          path: /hello
          method: get
    environment:
      distributionId: ${self:custom.CFDistributionId}
plugins:
  - serverless-webpack
  - serverless-offline
  - serverless-aws-nested-stacks

react-infrastructure.yml:

AWSTemplateFormatVersion: '2010-09-09'

Description: >
  Creates the infrastructure to host and expose a Single Page Application:
      - An Amazon S3 bucket for hosting the application
      - An Amazon CloudFront distribution to expose the application
      - An Amazon S3 bucket for hosting bucket and cloudfront access logs

Parameters:
  
  ServiceName:
    Description: 'Name of the Serverless service'
    Type: String

  EnvironmentStage:
    Description: The environment (dev, test, prod)
    Type: String
    AllowedValues:
      - dev
      - test
      - prod

Resources:
  # The Amazon S3 bucket into which our Single Page Application build files must be deployed
  S3Bucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketName: !Sub '${ServiceName}-react-app-${EnvironmentStage}'
      PublicAccessBlockConfiguration:
        BlockPublicAcls : true
        BlockPublicPolicy : true
        IgnorePublicAcls : true
        RestrictPublicBuckets : true
      LoggingConfiguration:
        DestinationBucketName: !Ref LoggingBucket
        LogFilePrefix: s3-access-logs
      VersioningConfiguration:
        Status: Enabled
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: 'AES256'

  # The Amazon S3 bucket policy for securing the bucket hosting the application
  BucketPolicy:
    Type: 'AWS::S3::BucketPolicy'
    Properties:
      PolicyDocument:
        Id: MyPolicy
        Version: 2012-10-17
        Statement:
          - Sid: PolicyForCloudFrontPrivateContent
            Effect: Allow
            Principal:
              CanonicalUser: !GetAtt CFOriginAccessIdentity.S3CanonicalUserId
            Action: 's3:GetObject*'
            Resource: !Join 
              - ''
              - - 'arn:aws:s3:::'
                - !Ref S3Bucket
                - /*
      Bucket: !Ref S3Bucket

  # The Amazon S3 bucket into which access logs from S3 (for the application) and CloudFront will be put
  LoggingBucket:
    #checkov:skip=CKV_AWS_18: "This bucket is private and only for storing logs"
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketName: !Sub '${ServiceName}-react-app-${EnvironmentStage}-logs'
      PublicAccessBlockConfiguration:
        BlockPublicAcls : true
        BlockPublicPolicy : true
        IgnorePublicAcls : true
        RestrictPublicBuckets : true
      AccessControl: LogDeliveryWrite
      VersioningConfiguration:
        Status: Enabled
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: 'AES256'
    DeletionPolicy: Delete

  # The Amazon CloudFront distribution exposing our Single Page Application
  CFDistribution:
    #checkov:skip=CKV_AWS_68: "For demo purposes and to reduce cost, no WAF is configured"
    Type: 'AWS::CloudFront::Distribution'
    DependsOn:
      - CFOriginAccessIdentity
    Properties:
      DistributionConfig:
        Origins:
        - DomainName: !GetAtt S3Bucket.RegionalDomainName
          Id: myS3Origin
          S3OriginConfig:
            OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${CFOriginAccessIdentity}"
        Enabled: 'true'
        DefaultRootObject: index.html
        DefaultCacheBehavior:
          AllowedMethods:
          - GET
          - HEAD
          - OPTIONS
          TargetOriginId: myS3Origin
          CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 # CachingOptimized
          OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf # CORS-S3Origin
          ViewerProtocolPolicy: redirect-to-https
        PriceClass: PriceClass_All
        Logging:
          Bucket: !GetAtt LoggingBucket.RegionalDomainName
          Prefix: 'cloudfront-access-logs'

  # The Amazon CloudFront origin access identity
  CFOriginAccessIdentity:
    Type: 'AWS::CloudFront::CloudFrontOriginAccessIdentity'
    DependsOn:
      - S3Bucket
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: !Sub 'access-identity-${ServiceName}-react-app-${EnvironmentStage}'

Outputs:
  ReactBucketName:
    Value: !Sub "${ServiceName}-react-app-${EnvironmentStage}-${CFOriginAccessIdentity}"
  CFDistributionId:
    Value: !Ref CFDistribution
    Export:
      Name: !Sub "${ServiceName}-${EnvironmentStage}-CFDistributionId"

@gio-trueper

Hi, I'm new to serverless and I'm still learning. I have the need for an ALB and a nat gateway to be shared so that other stacks that are going to have some lambdas can use them. I think your answer could be my guide but how do you command execute when deploying? Serverless automatically detects that the serverless.yml file needs react-infrasctructure.yml ?. really appreciate your help

arleyyap avatar Sep 09 '22 17:09 arleyyap