private async Task TestCURDelete(string reportName) { // ARRANGE string Json = GenerateDeleteJson(reportName); CustomResourceRequest Request = JsonConvert.DeserializeObject <CustomResourceRequest>(Json); TestLambdaLogger TestLogger = new TestLambdaLogger(); TestClientContext ClientContext = new TestClientContext(); SharedCredentialsFile Creds = new SharedCredentialsFile(); Creds.TryGetProfile($"{Environment.UserName}-dev", out CredentialProfile Profile); ImmutableCredentials Cr = Profile.GetAWSCredentials(Creds).GetCredentials(); TestLambdaContext Context = new TestLambdaContext() { FunctionName = "CostAndUsageReportResource", FunctionVersion = "1", Logger = TestLogger, ClientContext = ClientContext, InvokedFunctionArn = "arn:aws:lambda:us-east-1:123456789012:function:FunctionName" }; // ACT Entrypoint Ep = new Entrypoint(); await Ep.Execute(Request, Context); // ASSERT }
private void FailIfRequested(CustomResourceRequest <ResourceProperties> request) { if (request.ResourceProperties.ShouldFail) { throw new Exception(request.ResourceProperties.ErrorMessage); } }
public void DeleteCustomResourceRequestTest() { // ARRANGE string Json = @" { ""requestType"":""delete"", ""responseUrl"":""https://s3.us-east-1.amazonaws.com/presigned-url/response.txt?X-Amz-Date=20180531T182534Z&X-Amz-SignedHeaders=host&X-Amz-Credential=AKIAIYLQNVRRFNZOCFFR%2F20170720%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=604800&X-Amz-Security-Token=FQoDYXdzEJP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaDOLWx95j90zPxGh7WSLdAVnoYoKC4gjrrR1xbokFWRRwutmuAmOxaIVcQqOy%2Fqxy%2FXQt3Iz%2FohuEEmI7%2FHPzShy%2BfgQtvfUeDaojrAx5q8fG9P1KuIfcedfkiU%2BCxpM2foyCGlXzoZuNlcF8ohm%2BaM3wh4%2BxQ%2FpShLl18cKiKEiw0QF1UQGj%2FsiEqzoM81vOSUVWL9SpTTkVq8EQHY1chYKBkBWt7eIQcxjTI2dQeYOohlrbnZ5Y1%2F1cxPgrbk6PkNFO3whAoliSjyRC8e4TSjIY2j3V6d9fUy4%2Fp6nLZIf9wuERL7xW9PjE6eZbKOHnw8sF&X-Amz-Signature=a14b3065ab822105e8d7892eb5dcc455ddd603c61e47520774a7289178af9ecc"", ""stackId"":""arn:aws:cloudformation:us-east-1:123456789012:stack/stack-name/f449b250-b969-11e0-a185-5081d0136786"", ""requestId"":""12345678"", ""resourceType"":""Custom::TestResource"", ""logicalResourceId"":""MyTestResource"", ""physicalResourceId"":""TestResource1"", ""resourceProperties"":{ ""name"":""Value"", ""digits"":[1,2,3], ""address"":{ ""street"":""MyStreetName"", ""number"":1234, ""city"":""Washington D.C."" } } }"; Json = Json.Trim().Replace("\r", "").Replace("\n", "").Replace("\t", ""); // ACT CustomResourceRequest Request = JsonConvert.DeserializeObject <CustomResourceRequest>(Json); string Content = JsonConvert.SerializeObject(Request, Formatting.None); // ASSERT Assert.Equal(Json, Content, true, true, true); }
public async Task ShouldCallCreate_IfRequestTypeIsCreate( ServiceCollection serviceCollection, CustomResourceRequest <object> request, JsonSerializer serializer, ILogger logger, [Substitute] TestCustomResourceLambda lambda, [Substitute] IHttpClient httpClient ) { serviceCollection.AddSingleton <ISerializer>(serializer); serviceCollection.AddSingleton(httpClient); var serviceProvider = serviceCollection.BuildServiceProvider(); var cancellationToken = new CancellationToken(false); var host = new TestCustomResourceLambdaHost(lambdaHost => { lambdaHost.Lambda = lambda; lambdaHost.Scope = serviceProvider.CreateScope(); lambdaHost.Serializer = serializer; lambdaHost.Logger = logger; }); request.RequestType = CustomResourceRequestType.Create; using var inputStream = await StreamUtils.CreateJsonStream(request); await host.InvokeLambda(inputStream, cancellationToken); await lambda.DidNotReceiveWithAnyArgs().Update(default !, default);
private async Task RespondToCloudFormation <T>(CustomResourceRequest request, string physicalResourceId, T data = null, Exception exception = null) where T : class { var response = new CustomResourceResponse <T> { Status = exception != null ? "FAILED" : "SUCCESS", Reason = exception?.Message ?? string.Empty, PhysicalResourceId = physicalResourceId, StackId = request.StackId, RequestId = request.RequestId, LogicalResourceId = request.LogicalResourceId, Data = data }; try { var client = new HttpClient(); var body = JsonSerializer.Serialize(response); var jsonContent = new StringContent(body); jsonContent.Headers.Remove("Content-Type"); LambdaLogger.Log(body); var postResponse = await client.PutAsync(request.ResponseURL, jsonContent); postResponse.EnsureSuccessStatusCode(); } catch (Exception ex) { LambdaLogger.Log("Exception: " + ex); } }
public Task <Response> Update(CustomResourceRequest <Request> request, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var password = GeneratePassword(request.ResourceProperties?.Length ?? options.DefaultLength); var response = new Response(password); return(Task.FromResult(response)); }
public override async Task <CustomResourceResponse> CreateAsync(CustomResourceRequest request, ILambdaContext context) { try { context.LogInfo("Attempting to create a pipeline."); CreatePipelineRequest PipelineRequest = JsonConvert.DeserializeObject <CreatePipelineRequest>(JsonConvert.SerializeObject(request.ResourceProperties)); CreatePipelineResponse CreateResponse = await this._ETClient.CreatePipelineAsync(PipelineRequest); if ((int)CreateResponse.HttpStatusCode < 200 || (int)CreateResponse.HttpStatusCode > 299) { return(new CustomResourceResponse(CustomResourceResponse.RequestStatus.FAILED, $"Received HTTP status code {(int)CreateResponse.HttpStatusCode}.", request)); } else { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.SUCCESS, $"See the details in CloudWatch Log Stream: {context.LogStreamName}.", CreateResponse.Pipeline.Id, request.StackId, request.RequestId, request.LogicalResourceId, false, new Dictionary <string, object>() { { "Name", CreateResponse.Pipeline.Name }, { "Arn", CreateResponse.Pipeline.Arn }, { "Id", CreateResponse.Pipeline.Id } } )); } } catch (AmazonElasticTranscoderException e) { context.LogError(e); return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.FAILED, e.Message, Guid.NewGuid().ToString(), request.StackId, request.RequestId, request.LogicalResourceId )); } catch (Exception e) { context.LogError(e); return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.FAILED, e.Message, Guid.NewGuid().ToString(), request.StackId, request.RequestId, request.LogicalResourceId )); } }
public Task <ResponseData> Delete(CustomResourceRequest <ResourceProperties> request, CancellationToken cancellationToken = default) { FailIfRequested(request); return(Task.FromResult(new ResponseData { Id = request.ResourceProperties.Name, MethodCalled = "Delete", })); }
/// <summary> /// Entrypoint for the Lambda function, calls the correct create, update, or delete function /// </summary> /// <param name="request">The custom resource request</param> /// <param name="context">The ILambdaContext object</param> /// <returns></returns> public async Task Execute(CustomResourceRequest request, ILambdaContext context) { context.LogInfo($"Received request:\n{JsonConvert.SerializeObject(request)}"); CustomResourceResult Result = await this._Handler.ExecuteAsync(request, context); if (Result.IsSuccess) { context.LogInfo("Successfully ran custom resource handler."); } else { context.LogError("Custom resource handler failed to run successfully."); } }
public async Task DatabaseTableCustomResource(CustomResourceRequest <DatabaseTableRequestData> request, ILambdaContext context) { var physicalResourceId = "unassigned-physical-resource-id"; try { context.Logger.LogLine(JsonSerializer.Serialize(request)); DatabaseTableResponseData response = new DatabaseTableResponseData { Name = request.ResourceProperties.Name }; switch (request.RequestType) { case "Create": physicalResourceId = $"{request.StackId}/{request.ResourceType}/{request.LogicalResourceId}"; await CreateTable(request.ResourceProperties); break; case "Update": physicalResourceId = request.PhysicalResourceId; await UpdateTable(request.OldResourceProperties, request.ResourceProperties); break; case "Delete": physicalResourceId = request.PhysicalResourceId; await DropTable(request.ResourceProperties); break; default: throw new NotSupportedException($"Cannot handle RequestType {request.RequestType}"); } await RespondToCloudFormation(request, physicalResourceId, response); } catch (Exception ex) { await RespondToCloudFormation(request, physicalResourceId, ex); LambdaLogger.Log("Exception: " + ex); } }
/// <summary> /// Called when the Kinesis Stream Awaiter is created in the CF script, it will wait on the specified stream to enter /// ACTIVE status /// </summary> /// <param name="request"></param> /// <param name="context"></param> /// <returns></returns> public override async Task <CustomResourceResponse> CreateAsync(CustomResourceRequest request, ILambdaContext context) { if (request.ResourceProperties.ContainsKey("StreamName")) { context.LogInfo($"Beginning await for Kinesis stream {request.ResourceProperties["StreamName"]}."); DescribeStreamRequest Request = new DescribeStreamRequest() { StreamName = request.ResourceProperties["StreamName"].ToString() }; while (true) { if (context.RemainingTime.TotalMilliseconds < 1500) { return(new CustomResourceResponse(CustomResourceResponse.RequestStatus.FAILED, "Timeout waiting for stream to become active.", request)); } DescribeStreamResponse Response = await this._KinesisClient.DescribeStreamAsync(Request); if ((int)Response.HttpStatusCode < 300) { if (Response.StreamDescription.StreamStatus == StreamStatus.ACTIVE) { break; } } else { context.LogWarning($"Received an unsuccessful response to the describe stream request: {(int)Response.HttpStatusCode}."); } Thread.Sleep(_WaitTimeInMillis); } context.LogInfo($"Successfully created Kinesis stream {Request.StreamName}."); return(new CustomResourceResponse(CustomResourceResponse.RequestStatus.SUCCESS, "Created", Request.StreamName, request.StackId, request.RequestId, request.LogicalResourceId)); } else { return(new CustomResourceResponse(CustomResourceResponse.RequestStatus.FAILED, "The StreamName property was not provided.", "stream", request.StackId, request.RequestId, request.LogicalResourceId)); } }
private async Task TestCURCreateParquet(string reportName) { string Json = GenerateCreateJsonParquet(reportName); CustomResourceRequest Request = JsonConvert.DeserializeObject <CustomResourceRequest>(Json); TestLambdaLogger TestLogger = new TestLambdaLogger(); TestClientContext ClientContext = new TestClientContext(); TestLambdaContext Context = new TestLambdaContext() { FunctionName = "CostAndUsageReportResource", FunctionVersion = "1", Logger = TestLogger, ClientContext = ClientContext, InvokedFunctionArn = "arn:aws:lambda:us-east-1:123456789012:function:FunctionName" }; // ACT Entrypoint Ep = new Entrypoint(); await Ep.Execute(Request, Context); }
public Task <Response> Delete(CustomResourceRequest <Request> request, CancellationToken cancellationToken = default) { var response = new Response(string.Empty); return(Task.FromResult(response)); }
public virtual bool RequiresReplacement(CustomResourceRequest <TestLambdaMessage> request) { return(false); }
private async Task RespondToCloudFormation(CustomResourceRequest request, string physicalResourceId, Exception exception = null) { await RespondToCloudFormation <object>(request, physicalResourceId, exception : exception); }
public virtual Task <TestCustomResourceOutputData> Create(CustomResourceRequest <TestLambdaMessage> request, CancellationToken cancellationToken) { return(Task.FromResult((TestCustomResourceOutputData)null !)); }
public virtual void Validate(CustomResourceRequest <TestLambdaMessage> request) { }
public override async Task <CustomResourceResponse> DeleteAsync(CustomResourceRequest request, ILambdaContext context) { try { context.LogInfo("Attempting to delete a pipeline."); ListPipelinesRequest Listing = new ListPipelinesRequest(); List <Pipeline> Pipelines = new List <Pipeline>(); ListPipelinesResponse Pipes; do { Pipes = await this._ETClient.ListPipelinesAsync(Listing); Pipelines.AddRange(Pipes.Pipelines.Where(x => x.Name.Equals(request.ResourceProperties["Name"] as string) && x.InputBucket.Equals(request.ResourceProperties["InputBucket"]) && x.Role.Equals(request.ResourceProperties["Role"]) )); } while (Pipes.NextPageToken != null); if (Pipelines.Count > 1) { context.LogWarning($"{Pipelines.Count} pipelines were found matching the Name, InputBucket, and Role specified."); } if (Pipelines.Count > 0) { DeletePipelineRequest PipelineRequest = new DeletePipelineRequest() { Id = Pipelines.First().Id }; DeletePipelineResponse DeleteResponse = await this._ETClient.DeletePipelineAsync(PipelineRequest); if ((int)DeleteResponse.HttpStatusCode < 200 || (int)DeleteResponse.HttpStatusCode > 299) { return(new CustomResourceResponse(CustomResourceResponse.RequestStatus.FAILED, $"Received HTTP status code {(int)DeleteResponse.HttpStatusCode}.", request)); } else { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.SUCCESS, $"See the details in CloudWatch Log Stream: {context.LogStreamName}.", request, false )); } } else { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.SUCCESS, "No pipelines could be found with the matching characteristics.", request )); } } catch (AmazonElasticTranscoderException e) { // If the pipeline doesn't exist, consider it deleted if (e.StatusCode == HttpStatusCode.NotFound) { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.SUCCESS, $"See the details in CloudWatch Log Stream: {context.LogStreamName}.", request )); } else { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.FAILED, e.Message, request )); } } catch (Exception e) { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.FAILED, e.Message, request )); } }
/// <summary> /// Called when the Kinesis Stream Awaiter is deleted in the CF script, no action is taken /// </summary> /// <param name="request"></param> /// <param name="context"></param> /// <returns></returns> public override async Task <CustomResourceResponse> DeleteAsync(CustomResourceRequest request, ILambdaContext context) { context.LogInfo("Delete called on KinesisStreamAwaiter"); return(new CustomResourceResponse(CustomResourceResponse.RequestStatus.SUCCESS, "Deleted", request)); }
public override async Task <CustomResourceResponse> UpdateAsync(CustomResourceRequest request, ILambdaContext context) { try { context.LogInfo("Initiating update for pipeline."); UpdatePipelineRequest PipelineRequest = JsonConvert.DeserializeObject <UpdatePipelineRequest>(JsonConvert.SerializeObject(request.ResourceProperties)); ListPipelinesRequest Listing = new ListPipelinesRequest(); List <Pipeline> Pipelines = new List <Pipeline>(); ListPipelinesResponse Pipes; do { Pipes = await this._ETClient.ListPipelinesAsync(Listing); Pipelines.AddRange(Pipes.Pipelines.Where(x => x.Name.Equals(request.ResourceProperties["Name"] as string) && x.InputBucket.Equals(request.ResourceProperties["InputBucket"]) && x.Role.Equals(request.ResourceProperties["Role"]) )); } while (Pipes.NextPageToken != null); if (Pipelines.Count > 1) { context.LogWarning($"{Pipelines.Count} pipelines were found matching the Name, InputBucket, and Role specified."); } if (Pipelines.Count > 0) { PipelineRequest.Id = Pipelines.First().Id; UpdatePipelineResponse UpdateResponse = await this._ETClient.UpdatePipelineAsync(PipelineRequest); if ((int)UpdateResponse.HttpStatusCode < 200 || (int)UpdateResponse.HttpStatusCode > 299) { return(new CustomResourceResponse(CustomResourceResponse.RequestStatus.FAILED, $"Received HTTP status code {(int)UpdateResponse.HttpStatusCode}.", request)); } else { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.SUCCESS, $"See the details in CloudWatch Log Stream: {context.LogStreamName}.", request, false, new Dictionary <string, object>() { { "Name", UpdateResponse.Pipeline.Name }, { "Arn", UpdateResponse.Pipeline.Arn }, { "Id", UpdateResponse.Pipeline.Id } } )); } } else { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.FAILED, "No pipelines could be found with the matching characteristics.", request )); } } catch (AmazonElasticTranscoderException e) { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.FAILED, e.Message, request )); } catch (Exception e) { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.FAILED, e.Message, request )); } }
public async Task TestCreate() { // ARRANGE AWSConfigs.AWSProfilesLocation = $"{Environment.GetEnvironmentVariable("UserProfile")}\\.aws\\credentials"; string StreamName = "test-stream"; string PresignedUrlBucket = "pre-sign-url-bucket"; string AccountNumber = "123456789012"; string Region = "us-east-1"; IAmazonS3 S3Client = new AmazonS3Client(); GetPreSignedUrlRequest Req = new GetPreSignedUrlRequest() { BucketName = PresignedUrlBucket, Key = "result.txt", Expires = DateTime.Now.AddMinutes(2), Protocol = Protocol.HTTPS, Verb = HttpVerb.PUT }; string PreSignedUrl = S3Client.GetPreSignedURL(Req); string Json = $@" {{ ""requestType"":""create"", ""responseUrl"":""{PreSignedUrl}"", ""stackId"":""arn:aws:cloudformation:{Region}:{AccountNumber}:stack/stack-name/{Guid.NewGuid().ToString()}"", ""requestId"":""12345678"", ""resourceType"":""Custom::KinesisStreamAwaiter"", ""logicalResourceId"":""KinesisStreamAwaiter"", ""resourceProperties"":{{ ""StreamName"":""{StreamName}"" }} }}"; CustomResourceRequest Request = JsonConvert.DeserializeObject <CustomResourceRequest>(Json); TestLambdaLogger TestLogger = new TestLambdaLogger(); TestClientContext ClientContext = new TestClientContext(); TestLambdaContext Context = new TestLambdaContext() { FunctionName = "KinesisStreamAwaiter", FunctionVersion = "1", Logger = TestLogger, ClientContext = ClientContext, LogGroupName = "aws/lambda/KinesisStreamAwaiter", LogStreamName = Guid.NewGuid().ToString(), RemainingTime = TimeSpan.FromSeconds(300) }; Entrypoint Entrypoint = new Entrypoint(); // ACT IAmazonKinesis KinesisClient = new AmazonKinesisClient(); CreateStreamRequest CreateReq = new CreateStreamRequest() { ShardCount = 1, StreamName = StreamName }; CreateStreamResponse CreateResponse = await KinesisClient.CreateStreamAsync(CreateReq); try { CustomResourceResult Response = await Entrypoint.ExecuteAsync(Request, Context); // ASSERT Assert.True(Response.IsSuccess); } finally { DeleteStreamRequest DeleteReq = new DeleteStreamRequest() { StreamName = StreamName }; await KinesisClient.DeleteStreamAsync(DeleteReq); } }
public async Task CreateCustomResourceWithHandlerTest() { // ARRANGE string AccountNumber = "123456789012"; string Region = "us-east-1"; string InputBucket = $"{Environment.UserName}-rawvideo"; string OutputBucket = $"{Environment.UserName}-video"; string PresignedUrlBucket = $"{Environment.UserName}-presigned-url-test"; string ThumbnailBucket = $"{Environment.UserName}-thumbnails"; string IAMRole = $"arn:aws:iam::{AccountNumber}:role/LambdaElasticTranscoderPipeline"; string NotificationTopic = $"arn:aws:sns:{Region}:{AccountNumber}:ElasticTranscoderNotifications"; string Key = "result.txt"; AWSConfigs.AWSProfilesLocation = $"{Environment.GetEnvironmentVariable("UserProfile")}\\.aws\\credentials"; Mock <IAmazonS3> s3Client = new Mock <IAmazonS3>(); s3Client.Setup(x => x.GetPreSignedURL(It.IsAny <GetPreSignedUrlRequest>())).Returns($"https://{PresignedUrlBucket}.s3.amazonaws.com/{Key}?AWSAccessKeyId=AKIA1234567890123456&Expires=1559247929&Signature=OTgL4H7i%2FQOcTFpLM%2AV2LsFjONE%3D"); GetPreSignedUrlRequest preSignedUrlRequest = new GetPreSignedUrlRequest() { BucketName = PresignedUrlBucket, Key = Key, Expires = DateTime.Now.AddMinutes(2), Protocol = Protocol.HTTPS, Verb = HttpVerb.PUT }; string PreSignedUrl = s3Client.Object.GetPreSignedURL(preSignedUrlRequest); string Json = $@" {{ ""requestType"":""create"", ""responseUrl"":""{PreSignedUrl}"", ""stackId"":""arn:aws:cloudformation:{Region}:{AccountNumber}:stack/stack-name/{Guid.NewGuid().ToString()}"", ""requestId"":""12345678"", ""resourceType"":""Custom::TestResource"", ""logicalResourceId"":""MyTestResource"", ""resourceProperties"":{{ ""Role"":""{IAMRole}"", ""Name"":""TestPipeline"", ""InputBucket"":""{InputBucket}"", ""Notifications"":{{ ""Error"": ""{NotificationTopic}"", }}, ""ContentConfig"":{{ ""Bucket"":""{OutputBucket}"" }}, ""ThumbnailConfig"":{{ ""Bucket"":""{ThumbnailBucket}"" }} }} }}"; Json = Json.Trim().Replace("\r", "").Replace("\n", "").Replace("\t", ""); Func <CustomResourceRequest, ILambdaContext, Task <CustomResourceResponse> > Create = async(request, context) => { try { //AmazonElasticTranscoderConfig Config = new AmazonElasticTranscoderConfig(); //IAmazonElasticTranscoder Client = new AmazonElasticTranscoderClient(Config); Mock <IAmazonElasticTranscoder> mockClient = new Mock <IAmazonElasticTranscoder>(); mockClient.Setup(x => x.CreatePipelineAsync(It.IsAny <CreatePipelineRequest>(), default(CancellationToken))) .ReturnsAsync(new CreatePipelineResponse() { HttpStatusCode = HttpStatusCode.OK }); context.LogInfo("Attempting to create a pipeline."); CreatePipelineRequest PipelineRequest = JsonConvert.DeserializeObject <CreatePipelineRequest>(JsonConvert.SerializeObject(request.ResourceProperties)); CreatePipelineResponse CreateResponse = await mockClient.Object.CreatePipelineAsync(PipelineRequest); if ((int)CreateResponse.HttpStatusCode < 200 || (int)CreateResponse.HttpStatusCode > 299) { return(new CustomResourceResponse(CustomResourceResponse.RequestStatus.FAILED, $"Received HTTP status code {(int)CreateResponse.HttpStatusCode}.", request)); } else { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.SUCCESS, $"See the details in CloudWatch Log Stream: {context.LogStreamName}.", CreateResponse.Pipeline.Id, request.StackId, request.RequestId, request.LogicalResourceId, false, new Dictionary <string, object>() { { "Name", CreateResponse.Pipeline.Name }, { "Arn", CreateResponse.Pipeline.Arn }, { "Id", CreateResponse.Pipeline.Id } } )); } } catch (AmazonElasticTranscoderException e) { context.LogError(e); return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.FAILED, e.Message, Guid.NewGuid().ToString(), request.StackId, request.RequestId, request.LogicalResourceId )); } catch (Exception e) { context.LogError(e); return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.FAILED, e.Message, Guid.NewGuid().ToString(), request.StackId, request.RequestId, request.LogicalResourceId )); } }; Func <CustomResourceRequest, ILambdaContext, Task <CustomResourceResponse> > Update = async(request, context) => { try { context.LogInfo("Initiating update for pipeline."); UpdatePipelineRequest PipelineRequest = JsonConvert.DeserializeObject <UpdatePipelineRequest>(JsonConvert.SerializeObject(request.ResourceProperties)); ListPipelinesRequest Listing = new ListPipelinesRequest(); List <Pipeline> Pipelines = new List <Pipeline>(); ListPipelinesResponse Pipes; AmazonElasticTranscoderConfig Config = new AmazonElasticTranscoderConfig(); IAmazonElasticTranscoder Client = new AmazonElasticTranscoderClient(Config); do { Pipes = await Client.ListPipelinesAsync(Listing); Pipelines.AddRange(Pipes.Pipelines.Where(x => x.Name.Equals(request.ResourceProperties["Name"] as string) && x.InputBucket.Equals(request.ResourceProperties["InputBucket"]) && x.Role.Equals(request.ResourceProperties["Role"]) )); } while (Pipes.NextPageToken != null); if (Pipelines.Count > 1) { context.LogWarning($"{Pipelines.Count} pipelines were found matching the Name, InputBucket, and Role specified."); } if (Pipelines.Count > 0) { PipelineRequest.Id = Pipelines.First().Id; UpdatePipelineResponse UpdateResponse = await Client.UpdatePipelineAsync(PipelineRequest); if ((int)UpdateResponse.HttpStatusCode < 200 || (int)UpdateResponse.HttpStatusCode > 299) { return(new CustomResourceResponse(CustomResourceResponse.RequestStatus.FAILED, $"Received HTTP status code {(int)UpdateResponse.HttpStatusCode}.", request)); } else { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.SUCCESS, $"See the details in CloudWatch Log Stream: {context.LogStreamName}.", request, false, new Dictionary <string, object>() { { "Name", UpdateResponse.Pipeline.Name }, { "Arn", UpdateResponse.Pipeline.Arn }, { "Id", UpdateResponse.Pipeline.Id } } )); } } else { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.FAILED, "No pipelines could be found with the matching characteristics.", request )); } } catch (AmazonElasticTranscoderException e) { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.FAILED, e.Message, request )); } catch (Exception e) { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.FAILED, e.Message, request )); } }; Func <CustomResourceRequest, ILambdaContext, Task <CustomResourceResponse> > Delete = async(request, context) => { try { context.LogInfo("Attempting to delete a pipeline."); ListPipelinesRequest Listing = new ListPipelinesRequest(); List <Pipeline> Pipelines = new List <Pipeline>(); ListPipelinesResponse Pipes; AmazonElasticTranscoderConfig Config = new AmazonElasticTranscoderConfig(); IAmazonElasticTranscoder Client = new AmazonElasticTranscoderClient(Config); do { Pipes = await Client.ListPipelinesAsync(Listing); Pipelines.AddRange(Pipes.Pipelines.Where(x => x.Name.Equals(request.ResourceProperties["Name"] as string) && x.InputBucket.Equals(request.ResourceProperties["InputBucket"]) && x.Role.Equals(request.ResourceProperties["Role"]) )); } while (Pipes.NextPageToken != null); if (Pipelines.Count > 1) { context.LogWarning($"{Pipelines.Count} pipelines were found matching the Name, InputBucket, and Role specified."); } if (Pipelines.Count > 0) { DeletePipelineRequest PipelineRequest = new DeletePipelineRequest() { Id = Pipelines.First().Id }; DeletePipelineResponse DeleteResponse = await Client.DeletePipelineAsync(PipelineRequest); if ((int)DeleteResponse.HttpStatusCode < 200 || (int)DeleteResponse.HttpStatusCode > 299) { return(new CustomResourceResponse(CustomResourceResponse.RequestStatus.FAILED, $"Received HTTP status code {(int)DeleteResponse.HttpStatusCode}.", request)); } else { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.SUCCESS, $"See the details in CloudWatch Log Stream: {context.LogStreamName}.", request, false )); } } else { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.SUCCESS, "No pipelines could be found with the matching characteristics.", request )); } } catch (AmazonElasticTranscoderException e) { // If the pipeline doesn't exist, consider it deleted if (e.StatusCode == HttpStatusCode.NotFound) { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.SUCCESS, $"See the details in CloudWatch Log Stream: {context.LogStreamName}.", request )); } else { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.FAILED, e.Message, request )); } } catch (Exception e) { return(new CustomResourceResponse( CustomResourceResponse.RequestStatus.FAILED, e.Message, request )); } }; CustomResourceRequest customResourceRequest = JsonConvert.DeserializeObject <CustomResourceRequest>(Json); Mock <ICustomResourceHelper> mockHelper = new Mock <ICustomResourceHelper>(); mockHelper.Setup(x => x.PutCustomResourceResponseAsync(It.IsAny <CustomResourceRequest>(), It.IsAny <CustomResourceResponse>())) .ReturnsAsync(new CustomResourceResult(customResourceRequest, new CustomResourceResponse(RequestStatus.SUCCESS, "", customResourceRequest), new HttpResponseMessage(HttpStatusCode.OK))); ICustomResourceHandler Handler = new CustomResourceFactory(Create, Update, Delete, mockHelper.Object); TestLambdaLogger TestLogger = new TestLambdaLogger(); TestClientContext ClientContext = new TestClientContext(); TestLambdaContext Context = new TestLambdaContext() { FunctionName = "ElasticTranscoderPipelineCreation", FunctionVersion = "1", Logger = TestLogger, ClientContext = ClientContext, LogGroupName = "aws/lambda/ElasticTranscoderPipeline", LogStreamName = Guid.NewGuid().ToString() }; // ACT CustomResourceResult Response = await Handler.ExecuteAsync(customResourceRequest, Context); // ASSERT Assert.NotNull(Response); Assert.NotNull(Response.Response); Assert.NotNull(Response.S3Response); Assert.True(Response.IsSuccess); }