Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cfn-resources/private-endpoint-aws/cmd/resource/model.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 23 additions & 17 deletions cfn-resources/private-endpoint-aws/cmd/resource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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",
Expand All @@ -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),
Expand All @@ -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()

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -215,37 +219,39 @@ 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
}

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{}{
Expand Down
30 changes: 15 additions & 15 deletions cfn-resources/private-endpoint-aws/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ To declare this entity in your AWS CloudFormation template, use the following sy
"<a href="#endpointserviceid" title="EndpointServiceId">EndpointServiceId</a>" : <i>String</i>,
"<a href="#id" title="Id">Id</a>" : <i>String</i>,
"<a href="#enforceconnectionsuccess" title="EnforceConnectionSuccess">EnforceConnectionSuccess</a>" : <i>Boolean</i>,
"<a href="#connectionstatus" title="ConnectionStatus">ConnectionStatus</a>" : <i>String</i>,
"<a href="#errormessage" title="ErrorMessage">ErrorMessage</a>" : <i>String</i>
}
}
</pre>
Expand All @@ -33,8 +31,6 @@ Properties:
<a href="#endpointserviceid" title="EndpointServiceId">EndpointServiceId</a>: <i>String</i>
<a href="#id" title="Id">Id</a>: <i>String</i>
<a href="#enforceconnectionsuccess" title="EnforceConnectionSuccess">EnforceConnectionSuccess</a>: <i>Boolean</i>
<a href="#connectionstatus" title="ConnectionStatus">ConnectionStatus</a>: <i>String</i>
<a href="#errormessage" title="ErrorMessage">ErrorMessage</a>: <i>String</i>
</pre>

## Properties
Expand Down Expand Up @@ -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

Expand All @@ -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.

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
},
"required": [
"ProjectId",
"EndpointServiceId"
"EndpointServiceId",
"Id"
],
"properties": {
"Profile": {
Expand All @@ -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"
Expand All @@ -54,6 +63,12 @@
"/properties/Profile",
"/properties/Id"
],
"readOnlyProperties": [
"/properties/InterfaceEndpointId",
"/properties/DeleteRequested",
"/properties/ConnectionStatus",
"/properties/ErrorMessage"
],
"handlers": {
"create": {
"permissions": [
Expand Down
21 changes: 21 additions & 0 deletions examples/private-endpoint-aws/README.md
Original file line number Diff line number Diff line change
@@ -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 <PROJECT_ID>`)
3. AWSVPCEndpointId (format: vpce-xxxxxxxxx)
4. Profile (optional)
5. EnforceConnectionSuccess (optional)
87 changes: 87 additions & 0 deletions examples/private-endpoint-aws/private-endpoint-aws.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
}
}
}

Loading