0
votes

I have created a CloudFormation template which deploys an AWS Lambda function that is called by AWS Transfer to authenticate sftp users. The Lambda function returns a policy which AWS Transfer picks up and uses to lock down the sftp users permissions.

This policy includes the variables:

  • transfer:HomeFolder
  • transfer:HomeBucket
  • transfer:HomeDirectory

For documentation about what these variables do, see: Editing User Configuration - AWS Transfer for SFTP

When I try to deploy my CloudFormation template I get the following error:

An error occurred (ValidationError) when calling the CreateChangeSet operation: Template format error: Unresolved resource dependencies [transfer:HomeFolder, transfer:HomeBucket, transfer:HomeDirectory] in the Resources block of the template.

Is it possible to override the CloudFormation parameters for the policy variables: transfer:HomeFolder, transfer:HomeBucket, transfer:HomeDirectory?

Here is the Lambda part of my CloudFormation template:

GetUserConfigLambda:
  Type: AWS::Lambda::Function
  Properties:
    Code:
    ZipFile:
      Fn::Sub: |
      'use strict';

      const https = require('https');

      exports.handler = (event, context, callback) => {
          const data = JSON.stringify({
            username: event.username,
            password: event.password
          });

          const options = {
              hostname: '${Url}',
              path: '/api/v1.0/users',
              method: 'POST',
              headers: {
                  'Content-Type': 'application/json',
                  'Content-Length': data.length
              }
          };

          var policy = {
            "Version": "2012-10-17",
            "Statement": [
              {
                "Action": [
                    "s3:ListBucket",
                    "s3:GetBucketLocation"
                ],
                "Effect": "Allow",
                "Resource": [
                  "arn:aws:s3:::${transfer:HomeBucket}"
                ],
                "Condition": {
                  "StringLike": {
                    "s3:prefix": [
                      "${transfer:HomeFolder}/*",
                      "${transfer:HomeFolder}"
                    ]
                  }
                }
              },
              {
                "Effect": "Allow",
                "Action": [
                  "s3:PutObject",
                  "s3:GetObject",
                  "s3:DeleteObjectVersion",
                  "s3:DeleteObject",
                  "s3:GetObjectVersion"
                ],
                "Resource": "arn:aws:s3:::${transfer:HomeDirectory}*"
              },
              {
                "Action":[
                  "s3:PutObject"
                ],
                "Effect":"Deny",
                "Resource":"arn:aws:s3:::${transfer:HomeDirectory}/*/"
              }
            ]
          }

          var req = https.request(options, (res) => {
              console.log("Status code: ", res.statusCode);

              let data = '';
              res.on('data', (chunk) => {
                  data += chunk;
              }).on('end', () => {
                  let response = JSON.parse(data);
                  let lambdaResponse = {};

                  if('data' in response &&
                      'name' in response['data']) {
                      lambdaResponse = {
                          // Required.
                          Role: 'arn:aws:iam::669858054894:role/${UserRole}',

                          // JSON blob which further restrict this user's permissions.
                          Policy: JSON.stringify(policy),

                          // Home directory is a concatenation of home bucket and integration name.
                          HomeDirectory: "/${SftpS3Bucket}/" + response['data']['name'],

                          // Name of home bucket.
                          HomeBucket: "${SftpS3Bucket}"
                      }
                  }

                  callback(null, lambdaResponse);
              });
          }).on("error", (err) => {
              console.log("Error: ", err.message);

              callback(null, {});
          });

          req.end(data);
      };
1

1 Answers

0
votes

I suspect that CloudFormation might be getting confused about the ${xx} blocks, which have special meaning in a CloudFormation template.

I think you merely wish to pass those values through as text, which will then be interpreted within the IAM Policy.

You should be able to avoid this behaviour by breaking up the text string in Python, such as:

"arn:aws:s3:::$""{transfer:HomeBucket}"