I had the same issue. The only way I was able to solve it was by using custom resource lambda in CloudFormation. The lambda takes Id of DB Instance (in this case replica id) and uses boto3 to change it to Multi-Az replica.
This is the template for the lambda function:
# Create a python lambda function which
# can enable Multi-AZ for read replicas.
#
# This lambda is meant to be executed by custom resource
# from other template in CloudFormaton
---
Resources:
EnableMultiAz:
Type: AWS::Lambda::Function
Properties:
Description: Create an AMI of an instance
FunctionName: EnableMultiAzForDBInstance
Handler: index.lambda_handler
Role: !GetAtt LambdaExecutionRole.Arn
Runtime: python3.7
Timeout: 60
Code:
ZipFile: |
import json
import boto3
import logging
import time
from botocore.vendored import requests
logger = logging.getLogger()
logger.setLevel(logging.INFO)
SUCCESS = "SUCCESS"
FAILED = "FAILED"
# from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-lambda-function-code-cfnresponsemodule.html
def send(event, context, responseStatus, responseData, physicalResourceId=None, noEcho=False):
responseUrl = event['ResponseURL']
print(responseUrl)
responseBody = {
'Status': responseStatus,
'Reason': 'See ' + context.log_stream_name,
'PhysicalResourceId': physicalResourceId or context.log_stream_name,
'StackId': event['StackId'],
'RequestId': event['RequestId'],
'LogicalResourceId': event['LogicalResourceId'],
'NoEcho': noEcho,
'Data': responseData
}
json_responseBody = json.dumps(responseBody)
print("Response body:\n" + json_responseBody)
headers = {
'content-type' : '',
'content-length' : str(len(json_responseBody))
}
try:
response = requests.put(responseUrl,
data=json_responseBody,
headers=headers)
print("Status code: " + response.reason)
except Exception as e:
print("send(..) failed executing requests.put(..): " + str(e))
def lambda_handler(event, context):
print('event: ', json.dumps(event))
try:
return_data = {}
if 'RequestType' in event:
rds = boto3.client('rds')
dbinstance_id = event['ResourceProperties']['DBinstanceId']
if event['RequestType'] == 'Create':
r = rds.modify_db_instance(
DBInstanceIdentifier=dbinstance_id,
MultiAZ=True, ApplyImmediately=True)
send(event, context, SUCCESS, return_data)
except Exception as e:
logger.error('custom response lambda failed due to: ' + str(e))
send(event, context, FAILED, {})
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: lambda-execution-role-with-rds
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal: {'Service': ['lambda.amazonaws.com']}
Action: ['sts:AssumeRole']
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSLambdaExecute
Path: '/'
Policies:
- PolicyName: PassRolePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action: "iam:PassRole"
Resource: "*"
- PolicyName: EnableMultiAZReadReplica
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "rds:ModifyDBInstance"
Resource: "*"
Outputs:
LambdaArn:
Value: !GetAtt EnableMultiAz.Arn
LambdaRoleArn:
Value: !GetAtt LambdaExecutionRole.Arn
You place this lambda template in S3, and then the parent template you can:
# Define nested stack that creates the lambda
MyCustomLambdaToMakeMultiAzReplicas:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: "<url to lambda template in s3>"
TimeoutInMinutes: 1
# Define a custom resource using the lambda. Replica
# db instance id is passed to the lambda and the lambda is
# executed
EnableMultiAzForReplica:
Type: "Custom::CustomLambdaToMakeMultiAzReplicas"
Properties:
ServiceToken: !GetAtt MyCustomLambdaToMakeMultiAzReplicas.Outputs.LambdaArn
DBinstanceId: !Ref ReadReplicaDbInstanceId