Search code examples
jenkinsaws-cloudformationaws-codepipelineaws-secrets-manager

Deploying Jenkins to AWS using cloudformation and secrets manager


My objective is to build Jenkins as a docker image and deploy it to AWS Elastic Beanstalk.

To build the docker image I am using the Configuration as Code plugin and injecting all secrets via environment variables in the Dockerfile.

What I am trying to figure out now is how to automate this deployment using CloudFormation or CodePipeline.

My question is:

  • Can I fetch secrets from AWS Secrets Manager using either CloudFormation or CodePipeline and inject them as environment variables in the deployment to Elastic Beanstalk?

Solution

  • Cloudformation templates can recover secrets from Secrets Manager. It is somewhat ugly, but works pretty well. In general, I use a security.yaml nested stack to generate secrets for me in SM, then recover them in other stacks.

    I can't speak too much to EB, but if you are deploying that through CF, then this should help.

    Generating a secret in SM (CF security.yaml):

    Parameters:
      DeploymentEnvironment:
        Type: String
        Description: Deployment environment, e.g. prod, stage, qa, dev, or userdev
        Default: "dev"
    ...
    Resources:
    ...  
      RegistryDbAdminCreds:
        Type: 'AWS::SecretsManager::Secret'
        Properties:
          Name: !Sub "RegistryDbAdminCreds-${DeploymentEnvironment}"
          Description: "RDS master uid/password for artifact registry database."
          GenerateSecretString:
            SecretStringTemplate: '{"username": "artifactadmin"}'
            GenerateStringKey: "password"
            PasswordLength: 30
            ExcludeCharacters: '"@/\+//:*`"'
          Tags:
          -
            Key: AppName
            Value: RegistryDbAdminCreds
    

    Using the secret in another yaml:

    Parameters:
      DeploymentEnvironment:
        Type: String
        Description: Deployment environment, e.g. prod, stage, qa, dev, or userdev
        Default: "dev"
    ...
    Resources:
      DB:
        Type: 'AWS::RDS::DBInstance'
        DependsOn: security
        Properties:
          Engine: postgres
          DBInstanceClass: db.t2.small
          DBName: quilt
          MasterUsername: !Sub '{{resolve:secretsmanager:RegistryDbAdminCreds-${DeploymentEnvironment}:SecretString:username}}'
          MasterUserPassword: !Sub '{{resolve:secretsmanager:RegistryDbAdminCreds-${DeploymentEnvironment}:SecretString:password}}'
          StorageType: gp2
          AllocatedStorage: "100"
          PubliclyAccessible: true
          DBSubnetGroupName: !Ref SubnetGroup
          MultiAZ: true
          VPCSecurityGroups:
          - !GetAtt "network.Outputs.VPCSecurityGroup"
          Tags:
          - Key: Name
            Value: !Join [ '-', [ !Ref StackName, "dbinstance", !Ref DeploymentEnvironment ] ]
    

    The trick is in !Sub '{{resolve:secretsmanager:RegistryDbAdminCreds-${DeploymentEnvironment}:SecretString:username}}' and !Sub '{{resolve:secretsmanager:RegistryDbAdminCreds-${DeploymentEnvironment}:SecretString:password}}'