diff --git a/cfn-resources/private-endpoint-aws/cmd/resource/model.go b/cfn-resources/private-endpoint-aws/cmd/resource/model.go index bb6853444..d289f496e 100644 --- a/cfn-resources/private-endpoint-aws/cmd/resource/model.go +++ b/cfn-resources/private-endpoint-aws/cmd/resource/model.go @@ -9,6 +9,8 @@ type Model struct { EndpointServiceId *string `json:",omitempty"` Id *string `json:",omitempty"` EnforceConnectionSuccess *bool `json:",omitempty"` + InterfaceEndpointId *string `json:",omitempty"` + DeleteRequested *bool `json:",omitempty"` ConnectionStatus *string `json:",omitempty"` ErrorMessage *string `json:",omitempty"` } diff --git a/cfn-resources/private-endpoint-aws/cmd/resource/resource.go b/cfn-resources/private-endpoint-aws/cmd/resource/resource.go index 9da66335a..436981006 100644 --- a/cfn-resources/private-endpoint-aws/cmd/resource/resource.go +++ b/cfn-resources/private-endpoint-aws/cmd/resource/resource.go @@ -21,7 +21,7 @@ import ( "net/http" "strings" - admin20231115014 "go.mongodb.org/atlas-sdk/v20231115014/admin" + "go.mongodb.org/atlas-sdk/v20250312012/admin" "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/handler" "github.com/aws/aws-sdk-go-v2/aws" @@ -92,11 +92,13 @@ func Create(req handler.Request, prevModel *Model, currentModel *Model) (handler *privateEndpoint.ConnectionStatus == Rejected { return handler.ProgressEvent{ OperationStatus: handler.Failed, - Message: fmt.Sprintf("Connection was Rejected : %s", *privateEndpoint.ErrorMessage), + Message: fmt.Sprintf("Connection was Rejected: %s", *privateEndpoint.ErrorMessage), ResourceModel: currentModel, }, nil } + currentModel.completeByAtlasModel(*privateEndpoint) + return handler.ProgressEvent{ OperationStatus: handler.Success, Message: "Create Success", @@ -115,17 +117,17 @@ func Create(req handler.Request, prevModel *Model, currentModel *Model) (handler }}, nil } - endpointRequest := admin20231115014.CreateEndpointRequest{ + endpointRequest := admin.CreateEndpointRequest{ Id: currentModel.Id, } - privateEndpointRequest := client.Atlas20231115014.PrivateEndpointServicesApi.CreatePrivateEndpoint(context.Background(), *currentModel.ProjectId, + privateEndpointRequest := client.AtlasSDK.PrivateEndpointServicesApi.CreatePrivateEndpoint(context.Background(), *currentModel.ProjectId, CloudProvider, *currentModel.EndpointServiceId, &endpointRequest) _, response, err := privateEndpointRequest.Execute() defer response.Body.Close() if err != nil { - if response.StatusCode == http.StatusConflict { + if util.StatusConflict(response) { return progress_events.GetFailedEventByCode( fmt.Sprintf("error creating Serverless Private Endpoint %s", err.Error()), string(types.HandlerErrorCodeAlreadyExists), @@ -146,8 +148,8 @@ func Create(req handler.Request, prevModel *Model, currentModel *Model) (handler }}, nil } -func getPrivateEndpoint(client *util.MongoDBClient, model *Model) (*admin20231115014.PrivateLinkEndpoint, *http.Response, error) { - privateEndpointRequest := client.Atlas20231115014.PrivateEndpointServicesApi.GetPrivateEndpoint(context.Background(), *model.ProjectId, +func getPrivateEndpoint(client *util.MongoDBClient, model *Model) (*admin.PrivateLinkEndpoint, *http.Response, error) { + privateEndpointRequest := client.AtlasSDK.PrivateEndpointServicesApi.GetPrivateEndpoint(context.Background(), *model.ProjectId, CloudProvider, *model.Id, *model.EndpointServiceId) privateEndpoint, response, err := privateEndpointRequest.Execute() @@ -184,9 +186,11 @@ func Read(req handler.Request, prevModel *Model, currentModel *Model) (handler.P }, nil } -func (m *Model) completeByAtlasModel(privateEndpoint admin20231115014.PrivateLinkEndpoint) { - m.ErrorMessage = privateEndpoint.ErrorMessage +func (m *Model) completeByAtlasModel(privateEndpoint admin.PrivateLinkEndpoint) { + m.InterfaceEndpointId = privateEndpoint.InterfaceEndpointId + m.DeleteRequested = privateEndpoint.DeleteRequested m.ConnectionStatus = privateEndpoint.ConnectionStatus + m.ErrorMessage = privateEndpoint.ErrorMessage } // Update handles the Update event from the Cloudformation service. @@ -215,10 +219,10 @@ func Delete(req handler.Request, prevModel *Model, currentModel *Model) (handler _, response, peError := getPrivateEndpoint(client, currentModel) defer response.Body.Close() if peError != nil { - if response.StatusCode == http.StatusNotFound { + if util.StatusNotFound(response) { return handler.ProgressEvent{ OperationStatus: handler.Success, - Message: "Create Success", + Message: "Delete Success", }, nil } return progress_events.GetFailedEventByResponse("Error validating Private Endpoint deletion progress", response), nil @@ -226,26 +230,28 @@ func Delete(req handler.Request, prevModel *Model, currentModel *Model) (handler return handler.ProgressEvent{ OperationStatus: handler.InProgress, - Message: "Create in progress", + Message: "Delete in progress", CallbackDelaySeconds: 20, CallbackContext: map[string]interface{}{ "state": "deleting", }}, nil } - privateEndpointRequest := client.Atlas20231115014.PrivateEndpointServicesApi.DeletePrivateEndpoint(context.Background(), *currentModel.ProjectId, + privateEndpointRequest := client.AtlasSDK.PrivateEndpointServicesApi.DeletePrivateEndpoint(context.Background(), *currentModel.ProjectId, CloudProvider, *currentModel.Id, *currentModel.EndpointServiceId) - _, response, err := privateEndpointRequest.Execute() - defer response.Body.Close() + response, err := privateEndpointRequest.Execute() + if response != nil && response.Body != nil { + defer response.Body.Close() + } if err != nil { - return progress_events.GetFailedEventByResponse(fmt.Sprintf("error creating Serverless Private Endpoint %s", + return progress_events.GetFailedEventByResponse(fmt.Sprintf("error deleting Private Endpoint: %s", err.Error()), response), nil } return handler.ProgressEvent{ OperationStatus: handler.InProgress, - Message: "Create in progress", + Message: "Delete in progress", CallbackDelaySeconds: 20, ResourceModel: currentModel, CallbackContext: map[string]interface{}{ diff --git a/cfn-resources/private-endpoint-aws/docs/README.md b/cfn-resources/private-endpoint-aws/docs/README.md index 7e7913d23..11e0658dd 100644 --- a/cfn-resources/private-endpoint-aws/docs/README.md +++ b/cfn-resources/private-endpoint-aws/docs/README.md @@ -17,8 +17,6 @@ To declare this entity in your AWS CloudFormation template, use the following sy "EndpointServiceId" : String, "Id" : String, "EnforceConnectionSuccess" : Boolean, - "ConnectionStatus" : String, - "ErrorMessage" : String } } @@ -33,8 +31,6 @@ Properties: EndpointServiceId: String Id: String EnforceConnectionSuccess: Boolean - ConnectionStatus: String - ErrorMessage: String ## Properties @@ -73,7 +69,7 @@ _Update requires_: [Replacement](https://docs.aws.amazon.com/AWSCloudFormation/l Unique string that identifies the private endpoint. for AWS is the VPC endpoint ID, example: vpce-xxxxxxxx -_Required_: No +_Required_: Yes _Type_: String @@ -89,23 +85,27 @@ _Type_: Boolean _Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt) -#### ConnectionStatus +## Return Values -State of the Amazon Web Service PrivateLink connection when MongoDB Cloud received this request. +### Fn::GetAtt -_Required_: No +The `Fn::GetAtt` intrinsic function returns a value for a specified attribute of this type. The following are the available attributes and sample return values. -_Type_: String +For more information about using the `Fn::GetAtt` intrinsic function, see [Fn::GetAtt](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html). -_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt) +#### InterfaceEndpointId -#### ErrorMessage +Unique identifier of the AWS VPC interface endpoint (e.g., vpce-0d00c26273372c6ef). -Error message returned when requesting private connection resource. The resource returns null if the request succeeded. +#### DeleteRequested -_Required_: No +Indicates if Atlas received a request to remove the interface endpoint from the private endpoint connection. -_Type_: String +#### ConnectionStatus -_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt) +State of the Amazon Web Service PrivateLink connection when MongoDB Cloud received this request. + +#### ErrorMessage + +Error message returned when requesting private connection resource. The resource returns null if the request succeeded. diff --git a/cfn-resources/private-endpoint-aws/mongodb-atlas-privateendpointaws.json b/cfn-resources/private-endpoint-aws/mongodb-atlas-privateendpointaws.json index 10f394953..5bf030164 100644 --- a/cfn-resources/private-endpoint-aws/mongodb-atlas-privateendpointaws.json +++ b/cfn-resources/private-endpoint-aws/mongodb-atlas-privateendpointaws.json @@ -8,7 +8,8 @@ }, "required": [ "ProjectId", - "EndpointServiceId" + "EndpointServiceId", + "Id" ], "properties": { "Profile": { @@ -32,6 +33,14 @@ "description": "If this proper is set to TRUE, the cloud formation resource will return success Only if the private connection is Succeeded", "type": "boolean" }, + "InterfaceEndpointId": { + "description": "Unique identifier of the AWS VPC interface endpoint (e.g., vpce-0d00c26273372c6ef).", + "type": "string" + }, + "DeleteRequested": { + "description": "Indicates if Atlas received a request to remove the interface endpoint from the private endpoint connection.", + "type": "boolean" + }, "ConnectionStatus": { "description": "State of the Amazon Web Service PrivateLink connection when MongoDB Cloud received this request.", "type": "string" @@ -54,6 +63,12 @@ "/properties/Profile", "/properties/Id" ], + "readOnlyProperties": [ + "/properties/InterfaceEndpointId", + "/properties/DeleteRequested", + "/properties/ConnectionStatus", + "/properties/ErrorMessage" + ], "handlers": { "create": { "permissions": [ diff --git a/examples/private-endpoint-aws/README.md b/examples/private-endpoint-aws/README.md new file mode 100644 index 000000000..399be6bad --- /dev/null +++ b/examples/private-endpoint-aws/README.md @@ -0,0 +1,21 @@ +# How to create a MongoDB::Atlas::PrivateEndpointAWS + +## Step 1: Activate the PrivateEndpointAWS resource in CloudFormation + Step a: Create Role using [execution-role.yaml](https://github.com/mongodb/mongodbatlas-cloudformation-resources/blob/master/examples/execution-role.yaml) in CFN resources folder. + + Step b: Search for MongoDB::Atlas::PrivateEndpointAWS resource. + + (CloudFormation > Public extensions > choose 'Third party' > Search with " Execution name prefix = MongoDB " ) + + Step c: Select and activate + Enter the RoleArn that is created in step 1. + + Your PrivateEndpointAWS Resource is ready to use. + +## Step 2: Create template using [private-endpoint-aws.json](private-endpoint-aws.json) + Note: Make sure you are providing appropriate values for: + 1. MongoDBAtlasProjectId + 2. AtlasPrivateEndpointServiceId (get from: `atlas privateEndpoints aws list --projectId `) + 3. AWSVPCEndpointId (format: vpce-xxxxxxxxx) + 4. Profile (optional) + 5. EnforceConnectionSuccess (optional) \ No newline at end of file diff --git a/examples/private-endpoint-aws/private-endpoint-aws.json b/examples/private-endpoint-aws/private-endpoint-aws.json new file mode 100644 index 000000000..d814eb9ad --- /dev/null +++ b/examples/private-endpoint-aws/private-endpoint-aws.json @@ -0,0 +1,87 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "Test template for MongoDB::Atlas::PrivateEndpointAWS resource. Links an existing AWS VPC Endpoint to an existing Atlas Private Endpoint Service.", + "Parameters": { + "Profile": { + "Type": "String", + "Description": "Atlas Profile name for credential management. See profile-secret.yaml for setup instructions.", + "Default": "default" + }, + "MongoDBAtlasProjectId": { + "Type": "String", + "Description": "Unique 24-hexadecimal digit string that identifies your MongoDB Atlas project.", + "AllowedPattern": "^([a-f0-9]{24})$", + "ConstraintDescription": "Must be a valid 24-character hexadecimal MongoDB Atlas project ID." + }, + "AtlasPrivateEndpointServiceId": { + "Type": "String", + "Description": "The Atlas Private Endpoint Service ID (24-character hexadecimal). Get this from MongoDB::Atlas::PrivateEndpointService.Id or Atlas CLI.", + "AllowedPattern": "^([a-f0-9]{24})$", + "ConstraintDescription": "Must be a valid 24-character hexadecimal Atlas Private Endpoint Service ID." + }, + "AWSVPCEndpointId": { + "Type": "String", + "Description": "The AWS VPC Endpoint ID that you want to link to Atlas (e.g., vpce-0123456789abcdef0).", + "AllowedPattern": "^vpce-[a-z0-9]{8,17}$", + "ConstraintDescription": "Must be a valid AWS VPC Endpoint ID starting with 'vpce-'." + }, + "EnforceConnectionSuccess": { + "Type": "String", + "Description": "If true, CloudFormation will fail if the connection is REJECTED. If false, it succeeds even if REJECTED.", + "Default": "true", + "AllowedValues": ["true", "false"] + } + }, + "Resources": { + "AtlasPrivateEndpointConnection": { + "Type": "MongoDB::Atlas::PrivateEndpointAWS", + "Properties": { + "ProjectId": { + "Ref": "MongoDBAtlasProjectId" + }, + "EndpointServiceId": { + "Ref": "AtlasPrivateEndpointServiceId" + }, + "Id": { + "Ref": "AWSVPCEndpointId" + }, + "Profile": { + "Ref": "Profile" + }, + "EnforceConnectionSuccess": { + "Ref": "EnforceConnectionSuccess" + } + } + } + }, + "Outputs": { + "ConnectionStatus": { + "Description": "Status of the AWS PrivateLink connection. Returns one of: NONE, PENDING_ACCEPTANCE, PENDING, AVAILABLE, REJECTED, DELETING.", + "Value": { + "Fn::GetAtt": [ + "AtlasPrivateEndpointConnection", + "ConnectionStatus" + ] + } + }, + "InterfaceEndpointId": { + "Description": "Unique identifier of the interface endpoint.", + "Value": { + "Fn::GetAtt": [ + "AtlasPrivateEndpointConnection", + "InterfaceEndpointId" + ] + } + }, + "DeleteRequested": { + "Description": "Indicates if Atlas received a request to remove the interface endpoint from the private endpoint connection.", + "Value": { + "Fn::GetAtt": [ + "AtlasPrivateEndpointConnection", + "DeleteRequested" + ] + } + } + } +} +