Ejemplo n.º 1
0
        private void AddFunction(Function function, IDictionary <string, object> environmentRefVariables)
        {
            var environmentVariables = function.Environment.ToDictionary(kv => "STR_" + kv.Key.ToUpperInvariant(), kv => (dynamic)kv.Value);

            environmentVariables["TIER"]            = Settings.Tier;
            environmentVariables["MODULE"]          = _module.Name;
            environmentVariables["DEADLETTERQUEUE"] = Settings.DeadLetterQueueUrl;
            environmentVariables["LOGGINGTOPIC"]    = Settings.LoggingTopicArn;
            environmentVariables["LAMBDARUNTIME"]   = function.Runtime;
            foreach (var environmentRefVariable in environmentRefVariables)
            {
                environmentVariables[environmentRefVariable.Key] = environmentRefVariable.Value;
            }

            // check if function as a VPC configuration
            Lambda.FunctionTypes.VpcConfig vpcConfig = null;
            if (function.VPC != null)
            {
                vpcConfig = new Lambda.FunctionTypes.VpcConfig {
                    SubnetIds        = function.VPC.SubnetIds,
                    SecurityGroupIds = function.VPC.SecurityGroupIds
                };
            }

            // create function definition
            _stack.Add(function.Name, new Lambda.Function {
                FunctionName = ToAppResourceName(function.Name),
                Description  = function.Description,
                Runtime      = function.Runtime,
                Handler      = function.Handler,
                Timeout      = function.Timeout,
                MemorySize   = function.Memory,
                ReservedConcurrentExecutions = function.ReservedConcurrency,
                Role = Fn.GetAtt("ModuleRole", "Arn"),
                Code = new Lambda.FunctionTypes.Code {
                    S3Bucket = Settings.DeploymentBucketName,
                    S3Key    = $"{_module.Name}/{Path.GetFileName(function.PackagePath)}"
                },
                DeadLetterConfig = new Lambda.FunctionTypes.DeadLetterConfig {
                    TargetArn = Settings.DeadLetterQueueArn
                },
                Environment = new Lambda.FunctionTypes.Environment {
                    Variables = environmentVariables
                },
                VpcConfig = vpcConfig,
                Tags      = new List <Tag> {
                    new Tag {
                        Key   = "lambdasharp:tier",
                        Value = Settings.Tier
                    },
                    new Tag {
                        Key   = "lambdasharp:module",
                        Value = _module.Name
                    }
                }
            });

            // check if function is exported
            if (function.Export != null)
            {
                var export = function.Export.StartsWith("/")
                    ? function.Export
                    : $"/{Settings.Tier}/{_module.Name}/{function.Export}";
                _stack.Add(function.Name + "SsmParameter", new SSM.Parameter {
                    Name        = export,
                    Description = function.Description,
                    Type        = "String",
                    Value       = Fn.Ref(function.Name)
                });
            }

            // check if function has any SNS topic event sources
            var topicSources = function.Sources.OfType <TopicSource>();

            if (topicSources.Any())
            {
                foreach (var topicSource in topicSources)
                {
                    // find the resource that matches the declared topic name
                    var parameter = _module.Parameters
                                    .OfType <AResourceParameter>()
                                    .FirstOrDefault(p => p.Name == topicSource.TopicName);

                    // determine how to reference the resource
                    object resourceReference;
                    switch (parameter)
                    {
                    case ReferencedResourceParameter reference:
                        resourceReference = reference.Resource.ResourceArn;
                        break;

                    case CloudFormationResourceParameter cloudformation:
                        resourceReference = Fn.Ref(topicSource.TopicName);
                        break;

                    default:
                        throw new ArgumentOutOfRangeException(
                                  nameof(parameter),
                                  parameter?.GetType().Name ?? "<null>",
                                  "parameter resource type must be either ReferencedResourceParameter or CloudFormationResourceParameter"
                                  );
                    }
                    _stack.Add($"{function.Name}{topicSource.TopicName}SnsPermission", new Lambda.Permission {
                        Action       = "lambda:InvokeFunction",
                        SourceArn    = resourceReference,
                        FunctionName = Fn.GetAtt(function.Name, "Arn"),
                        Principal    = "sns.amazonaws.com"
                    });
                    _stack.Add($"{function.Name}{topicSource.TopicName}Subscription", new SNS.Subscription {
                        Endpoint = Fn.GetAtt(function.Name, "Arn"),
                        Protocol = "lambda",
                        TopicArn = resourceReference
                    });
                }
            }

            // check if function has any API gateway event sources
            var scheduleSources = function.Sources.OfType <ScheduleSource>().ToList();

            if (scheduleSources.Any())
            {
                for (var i = 0; i < scheduleSources.Count; ++i)
                {
                    var name = function.Name + "ScheduleEvent" + (i + 1).ToString("00");
                    _stack.Add(name, new Events.Rule {
                        ScheduleExpression = scheduleSources[i].Expression,
                        Targets            = new List <Events.RuleTypes.Target> {
                            new Events.RuleTypes.Target {
                                Id  = ToAppResourceName(name),
                                Arn = Fn.GetAtt(function.Name, "Arn"),
                                InputTransformer = new Events.RuleTypes.InputTransformer {
                                    InputPathsMap = new Dictionary <string, dynamic> {
                                        ["version"] = "$.version",
                                        ["id"]      = "$.id",
                                        ["source"]  = "$.source",
                                        ["account"] = "$.account",
                                        ["time"]    = "$.time",
                                        ["region"]  = "$.region"
                                    },
                                    InputTemplate =
                                        @"{
  ""Version"": <version>,
  ""Id"": <id>,
  ""Source"": <source>,
  ""Account"": <account>,
  ""Time"": <time>,
  ""Region"": <region>,
  ""tName"": """ + scheduleSources[i].Name + @"""
}"
                                }
                            }
                        }
                    });
                }
                _stack.Add(function.Name + "ScheduleEventPermission", new Lambda.Permission {
                    Action        = "lambda:InvokeFunction",
                    SourceAccount = Settings.AwsAccountId,
                    FunctionName  = Fn.GetAtt(function.Name, "Arn"),
                    Principal     = "events.amazonaws.com"
                });
            }

            // check if function has any API gateway event sources
            var apiSources = function.Sources.OfType <ApiGatewaySource>().ToList();

            if (apiSources.Any())
            {
                foreach (var apiEvent in apiSources)
                {
                    _apiGatewayRoutes.Add(new ApiRoute {
                        Method         = apiEvent.Method,
                        Path           = apiEvent.Path,
                        Integration    = apiEvent.Integration,
                        Function       = function,
                        OperationName  = apiEvent.OperationName,
                        ApiKeyRequired = apiEvent.ApiKeyRequired
                    });
                }
            }

            // check if function has any S3 event sources
            var s3Sources = function.Sources.OfType <S3Source>().ToList();

            if (s3Sources.Any())
            {
                foreach (var grp in s3Sources.ToLookup(source => source.Bucket))
                {
                    var functionS3Permission   = $"{function.Name}{grp.Key}S3Permission";
                    var functionS3Subscription = $"{function.Name}{grp.Key}S3Subscription";
                    _stack.Add(functionS3Permission, new Lambda.Permission {
                        Action        = "lambda:InvokeFunction",
                        SourceAccount = Settings.AwsAccountId,
                        SourceArn     = Fn.GetAtt(grp.Key, "Arn"),
                        FunctionName  = Fn.GetAtt(function.Name, "Arn"),
                        Principal     = "s3.amazonaws.com"
                    });
                    _stack.Add(functionS3Subscription, new Model.CustomResource("Custom::LambdaSharpS3Subscriber")
                    {
                        ["ServiceToken"] = Settings.S3SubscriberCustomResourceTopicArn,
                        ["BucketName"]   = Fn.Ref(grp.Key),
                        ["FunctionArn"]  = Fn.GetAtt(function.Name, "Arn"),
                        ["Filters"]      = grp.Select(source => {
                            var filter = new Dictionary <string, object>()
                            {
                                ["Events"] = source.Events,
                            };
                            if (source.Prefix != null)
                            {
                                filter["Prefix"] = source.Prefix;
                            }
                            if (source.Suffix != null)
                            {
                                filter["Suffix"] = source.Suffix;
                            }
                            return(filter);
                        }).ToList()
                    });
                    _stack.AddDependsOn(functionS3Subscription, functionS3Permission);
                }
            }

            // check if function has any SQS event sources
            var sqsSources = function.Sources.OfType <SqsSource>().ToList();

            if (sqsSources.Any())
            {
                foreach (var source in sqsSources)
                {
                    _stack.Add($"{function.Name}{source.Queue}EventMapping", new Lambda.EventSourceMapping {
                        BatchSize      = source.BatchSize,
                        Enabled        = true,
                        EventSourceArn = Fn.GetAtt(source.Queue, "Arn"),
                        FunctionName   = Fn.Ref(function.Name)
                    });
                }
            }

            // check if function has any Alexa event sources
            var alexaSources = function.Sources.OfType <AlexaSource>().ToList();

            if (alexaSources.Any())
            {
                var index = 0;
                foreach (var source in alexaSources)
                {
                    ++index;
                    var suffix = (source.EventSourceToken != null)
                        ? source.EventSourceToken.ToMD5Hash().Substring(0, 7)
                        : index.ToString();
                    _stack.Add($"{function.Name}AlexaPermission{suffix}", new Lambda.Permission {
                        Action           = "lambda:InvokeFunction",
                        FunctionName     = Fn.GetAtt(function.Name, "Arn"),
                        Principal        = "alexa-appkit.amazon.com",
                        EventSourceToken = source.EventSourceToken
                    });
                }
            }

            // check if function has any DynamoDB event sources
            var dynamoDbSources = function.Sources.OfType <DynamoDBSource>().ToList();

            if (dynamoDbSources.Any())
            {
                foreach (var source in dynamoDbSources)
                {
                    _stack.Add($"{function.Name}{source.DynamoDB}EventMapping", new Lambda.EventSourceMapping {
                        BatchSize        = source.BatchSize,
                        StartingPosition = source.StartingPosition,
                        Enabled          = true,
                        EventSourceArn   = Fn.GetAtt(source.DynamoDB, "StreamArn"),
                        FunctionName     = Fn.Ref(function.Name)
                    });
                }
            }

            // check if function has any Kinesis event sources
            var kinesisSources = function.Sources.OfType <KinesisSource>().ToList();

            if (kinesisSources.Any())
            {
                foreach (var source in kinesisSources)
                {
                    _stack.Add($"{function.Name}{source.Kinesis}EventMapping", new Lambda.EventSourceMapping {
                        BatchSize        = source.BatchSize,
                        StartingPosition = source.StartingPosition,
                        Enabled          = true,
                        EventSourceArn   = Fn.GetAtt(source.Kinesis, "Arn"),
                        FunctionName     = Fn.Ref(function.Name)
                    });
                }
            }

            // check if function has any CloudFormation Macro event sources
            var macroSources = function.Sources.OfType <MacroSource>().ToList();

            if (macroSources.Any())
            {
                foreach (var source in macroSources)
                {
                    _stack.Add($"{function.Name}{source.MacroName}Macro", new CustomResource("AWS::CloudFormation::Macro")
                    {
                        ["Name"]         = $"{Settings.Tier}-{source.MacroName}",
                        ["FunctionName"] = Fn.Ref(function.Name)
                    });
                }
            }
        }
Ejemplo n.º 2
0
        private void AddFunction(Function function, IDictionary <string, object> environmentRefVariables)
        {
            var environmentVariables = function.Environment.ToDictionary(kv => kv.Key, kv => (dynamic)kv.Value);

            environmentVariables["TIER"]            = _module.Settings.Tier;
            environmentVariables["MODULE"]          = _module.Name;
            environmentVariables["DEADLETTERQUEUE"] = _module.Settings.DeadLetterQueueUrl;
            environmentVariables["LOGGINGTOPIC"]    = _module.Settings.LoggingTopicArn;
            environmentVariables["LAMBDARUNTIME"]   = function.Runtime;
            foreach (var environmentRefVariable in environmentRefVariables)
            {
                environmentVariables[environmentRefVariable.Key] = environmentRefVariable.Value;
            }

            // check if function as a VPC configuration
            Lambda.FunctionTypes.VpcConfig vpcConfig = null;
            if (function.VPC != null)
            {
                vpcConfig = new Lambda.FunctionTypes.VpcConfig {
                    SubnetIds        = function.VPC.SubnetIds,
                    SecurityGroupIds = function.VPC.SecurityGroupIds
                };
            }

            // create function definition
            _stack.Add(function.Name, new Lambda.Function {
                FunctionName = ToAppResourceName(function.Name),
                Description  = function.Description,
                Runtime      = function.Runtime,
                Handler      = function.Handler,
                Timeout      = function.Timeout,
                MemorySize   = function.Memory,
                ReservedConcurrentExecutions = function.ReservedConcurrency,
                Role = Fn.GetAtt("ModuleRole", "Arn"),
                Code = new Lambda.FunctionTypes.Code {
                    S3Bucket = _module.Settings.DeploymentBucketName,
                    S3Key    = function.PackageS3Key
                },
                DeadLetterConfig = new Lambda.FunctionTypes.DeadLetterConfig {
                    TargetArn = _module.Settings.DeadLetterQueueArn
                },
                Environment = new Lambda.FunctionTypes.Environment {
                    Variables = environmentVariables
                },
                VpcConfig = vpcConfig,
                Tags      = new List <Tag> {
                    new Tag {
                        Key   = "lambdasharp:tier",
                        Value = _module.Settings.Tier
                    },
                    new Tag {
                        Key   = "lambdasharp:module",
                        Value = _module.Name
                    }
                }
            });

            // check if function has any SNS topic event sources
            var topicSources = function.Sources.OfType <TopicSource>();

            if (topicSources.Any())
            {
                foreach (var topicSource in topicSources)
                {
                    // find the resource that matches the declared topic name
                    var parameter = _module.Parameters
                                    .OfType <AResourceParameter>()
                                    .FirstOrDefault(p => p.Name == topicSource.TopicName);

                    // determine how to reference the resource
                    object resourceReference;
                    switch (parameter)
                    {
                    case ReferencedResourceParameter reference:
                        resourceReference = reference.Resource.ResourceArn;
                        break;

                    case CloudFormationResourceParameter cloudformation:
                        resourceReference = Fn.Ref(topicSource.TopicName);
                        break;

                    default:
                        throw new ArgumentOutOfRangeException(
                                  nameof(parameter),
                                  parameter?.GetType().Name ?? "<null>",
                                  "parameter resource type must be either ReferencedResourceParameter or CloudFormationResourceParameter"
                                  );
                    }
                    _stack.Add($"{function.Name}{topicSource.TopicName}SnsPermission", new Lambda.Permission {
                        Action       = "lambda:InvokeFunction",
                        SourceArn    = resourceReference,
                        FunctionName = Fn.GetAtt(function.Name, "Arn"),
                        Principal    = "sns.amazonaws.com"
                    });
                    _stack.Add($"{function.Name}{topicSource.TopicName}Subscription", new SNS.Subscription {
                        Endpoint = Fn.GetAtt(function.Name, "Arn"),
                        Protocol = "lambda",
                        TopicArn = resourceReference
                    });
                }
            }

            // check if function has any API gateway event sources
            var scheduleSources = function.Sources.OfType <ScheduleSource>().ToList();

            if (scheduleSources.Any())
            {
                for (var i = 0; i < scheduleSources.Count; ++i)
                {
                    var name = function.Name + "ScheduleEvent" + (i + 1).ToString("00");
                    _stack.Add(name, new Events.Rule {
                        ScheduleExpression = scheduleSources[i].Expression,
                        Targets            = new List <Events.RuleTypes.Target> {
                            new Events.RuleTypes.Target {
                                Id  = ToAppResourceName(name),
                                Arn = Fn.GetAtt(function.Name, "Arn"),
                                InputTransformer = new Events.RuleTypes.InputTransformer {
                                    InputPathsMap = new Dictionary <string, dynamic> {
                                        ["version"] = "$.version",
                                        ["id"]      = "$.id",
                                        ["source"]  = "$.source",
                                        ["account"] = "$.account",
                                        ["time"]    = "$.time",
                                        ["region"]  = "$.region"
                                    },
                                    InputTemplate =
                                        @"{
  ""Version"": <version>,
  ""Id"": <id>,
  ""Source"": <source>,
  ""Account"": <account>,
  ""Time"": <time>,
  ""Region"": <region>,
  ""tName"": """ + scheduleSources[i].Name + @"""
}"
                                }
                            }
                        }
                    });
                }
                _stack.Add(function.Name + "ScheduleEventPermission", new Lambda.Permission {
                    Action        = "lambda:InvokeFunction",
                    SourceAccount = _module.Settings.AwsAccountId,
                    FunctionName  = Fn.GetAtt(function.Name, "Arn"),
                    Principal     = "events.amazonaws.com"
                });
            }

            // check if function has any API gateway event sources
            var apiSources = function.Sources.OfType <ApiGatewaySource>().ToList();

            if (apiSources.Any())
            {
                foreach (var apiEvent in apiSources)
                {
                    _apiGatewayRoutes.Add(new ApiRoute {
                        Method      = apiEvent.Method,
                        Path        = apiEvent.Path,
                        Integration = apiEvent.Integration,
                        Function    = function
                    });
                }
            }

            // check if function has any S3 event sources
            var s3Sources = function.Sources.OfType <S3Source>().ToList();

            if (s3Sources.Any())
            {
                foreach (var source in s3Sources.Distinct(source => source.BucketArn))
                {
                    var permissionLogicalId = $"{function.Name}{source.Bucket}S3Permission";
                    _stack.Add(permissionLogicalId, new Lambda.Permission {
                        Action        = "lambda:InvokeFunction",
                        SourceAccount = _module.Settings.AwsAccountId,
                        SourceArn     = source.BucketArn,
                        FunctionName  = Fn.GetAtt(function.Name, "Arn"),
                        Principal     = "s3.amazonaws.com"
                    });
                    _stack.AddDependsOn(source.Bucket, permissionLogicalId);
                }
            }

            // check if function has any SQS event sources
            var sqsSources = function.Sources.OfType <SqsSource>().ToList();

            if (sqsSources.Any())
            {
                foreach (var source in sqsSources)
                {
                    _stack.Add($"{function.Name}{source.Queue}EventMapping", new Lambda.EventSourceMapping {
                        BatchSize      = source.BatchSize,
                        Enabled        = true,
                        EventSourceArn = Fn.GetAtt(source.Queue, "Arn"),
                        FunctionName   = Fn.Ref(function.Name)
                    });
                }
            }

            // check if function has any Alexa event sources
            var alexaSources = function.Sources.OfType <AlexaSource>().ToList();

            if (alexaSources.Any())
            {
                var index = 0;
                foreach (var source in alexaSources)
                {
                    ++index;
                    var suffix = (source.EventSourceToken != null)
                        ? source.EventSourceToken.ToMD5Hash().Substring(0, 7)
                        : index.ToString();
                    _stack.Add($"{function.Name}AlexaPermission{suffix}", new Lambda.Permission {
                        Action           = "lambda:InvokeFunction",
                        FunctionName     = Fn.GetAtt(function.Name, "Arn"),
                        Principal        = "alexa-appkit.amazon.com",
                        EventSourceToken = source.EventSourceToken
                    });
                }
            }
        }