AWS Database Blog

Deploy Amazon RDS Custom for Oracle using Terraform modules

Amazon Relational Database Service (Amazon RDS) Custom for Oracle is a managed database service for applications that require customization of the underlying operating system and database environment. It provides the benefits of RDS automation with the elevated access needed for legacy, packaged, and custom applications. With Amazon RDS Custom for Oracle, you have the freedom to customize your database and operating system, allowing you to apply special patches and modify database software settings to support third-party applications that necessitate privileged access. By taking advantage of the efficiency of a managed service, you can focus on business-impacting strategic activities.

In this post, we provide guidance on how to deploy Amazon RDS Custom for Oracle using HashiCorp Terraform and the AWS published AWS RDS Custom for Oracle Terraform module. Terraform is an infrastructure as code (IaC) tool that lets you provision resources safely and efficiently. The AWS RDS Custom for Oracle Terraform module (CfO module) provides prescriptive deployment configurations, and automates the deployment and configuration of service required resource types such as AWS Identity and Access Management (IAM) roles, instance profiles, database subnet groups, security groups, and VPC endpoints.

Solution overview

With Terraform and the CfO module, we can automate the creation of an RDS Custom for Oracle primary instance and two read replicas across three Availability Zones, one for the primary instance and two for the replicas. The following diagram illustrates this architecture.

The high-level steps to implement this solution are as follows:

  1. Create and initialize a Terraform project.
  2. Create a customer managed AWS Key Management Service (AWS KMS) key (symmetric).
  3. Create custom engine version (CEV) for Amazon RDS Custom for Oracle.
  4. Create a VPC with three subnets across three Availability Zones.
  5. Create an RDS Custom for Oracle primary instance.
  6. Create two RDS Custom for Oracle read replicas.

Prerequisites

This solution requires the following prerequisites:

Create and initialize a Terraform project

For this post, we create a new Terraform project and initialize it. You can work in a local folder of your choosing. Complete the following steps:

  1. In your preferred terminal, create a new folder namedrdscfo:
    mkdir rdscfo 
    cd rdscfo
  2. In your preferred text editor, add a new file with the following Terraform code:
    terraform {
      required_version = ">= 1.0.0"
      required_providers {
        aws = {
          source  = "hashicorp/aws"
          version = ">= 4.0.0"
        }
      }
    }
  3. Save the file in the root of the rdscfo folder and name it main.tf.
  4. In your terminal, run the following command to initialize the Terraform working directory:
    terraform init

    The output will contain a successful message like the following:
    “Terraform has been successfully initialized”

  5. In your terminal, validate the syntax for your Terraform files:
    terraform validate

Create a customer managed KMS key

In this step, you use Terraform to create a customer managed key (CMK), which outputs the CMK Amazon Resource Name (ARN). Complete the following steps:

  1. In your text editor, add the following Terraform code to your main.tf file:
    #Create a customer managed key
    resource "aws_kms_key" "rdscfo_kms_key" {
      description = "KMS symmetric key for RDS Custom for Oracle"
    }
    
    output "kms_key_arn" {
      description = "ARN for KMS key"
      value       = aws_kms_key.rdscfo_kms_key.arn
    }
  2. Save the file.
  3. In your terminal, apply the Terraform configuration to create the CMK:
    terraform apply

    Your terminal should have an output similar to the following:

    aws_kms_key.rdscfo_kms_key: Creating...
    aws_kms_key.rdscfo_kms_key: Creation complete after 1s [id=a1b2c3d4-5678-90ab-cdef-EXAMPLE11111]
    
    Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
    
    Outputs:
    
    kms_key_arn = "arn:aws:kms:us-west-2:111122223333:key/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"
  4. Note the CMK ARN down.
    This is required when creating a CEV. In this example, from the preceding output, the CMK ARN is

    "arn:aws:kms:us-west-2:111122223333:key/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111".

Create the CEV

CEV creation is not yet supported in Terraform. We use the AWS Management Console to create a CEV. Note that CEV creation can take 2 hours or longer. Complete the following steps:

  1. Create a CEV
  2. When prompted for a KMS key, provide the ARN from the previous section.

Create a VPC with three subnets across three Availability Zones

Next, create the networking resources needed to host your RDS Custom for Oracle instances:

  1. Add the following Terraform code to your main.tf file:
    You can change the argument values to your own. We have included comments (starting with #) for most of these arguments with example names for the VPC, CIDR block, and so on:

    # Create a VPC and subnets to deploy the RDS instance and replicas
 
    module "vpc" {
    
 source  = "aws-ia/vpc/aws"
    
 version = ">= 4.0.0"
    
      name   = "multi-az-vpc-01" # Modify VPC name as needed
    
 cidr_block = "10.2.0.0/20" # Modify CIDR block as needed
    
 az_count   = 3
    
      subnets = {
      
 private = { netmask = 24 }
    
 }
    }
    
    output "subnet_config" {
    
 description = "Created subnets."
    
 value       = flatten([for _, value in module.vpc.private_subnet_attributes_by_az : {subnet_id = value.id, availability_zone = value.availability_zone}])
    }
  2. Save the file.
  3. In your terminal, initialize Terraform again to install the VPC module:
    terraform init
  4. Preview the changes that Terraform intends to make by using the plan command:
    terraform plan
  5. Apply the Terraform configuration to create the VPC and subnets:
    terraform apply

Create an RDS Custom for Oracle primary instance

You can use Terraform data sources to look up the instance types supported for your custom engine version in the respective operating Region. Use the CfO module (published by AWS) to create an RDS Custom for Oracle primary instance.

  1. Add the following Terraform code to your main.tf file:
    You can change the argument values to your own.

    # Look up the instance type suppored for the CEV specified.
    data "aws_rds_orderable_db_instance" "custom-oracle" {
      engine                     = "custom-oracle-ee" # CEV engine to be used
      engine_version             = "19.2021-01.custom_oracle_home"    # Provide CEV version/name of CEV created earlier
      license_model              = "bring-your-own-license"
      storage_type               = "gp3"
      preferred_instance_classes = ["db.r5.xlarge", "db.r5.2xlarge", "db.r5.4xlarge"]
    }

    Next, you create a primary instance using the CfO module. You can customize the attributes of the module to meet your needs, such as more or less storage and user names and passwords that meet your organization’s security policies.

  2. Edit main.tf to include the module rds_custom_for_oracle with instance parameter values as shown in the following code (you can change the argument values to your own):
    Note: You can change argument values with your own. We have included comment (starting with #) for most of these arguments.

    module "rds_custom_for_oracle" {
      source  = "aws-ia/rds-custom-for-oracle/aws"
      version = ">= 1.0.0"
    
      vpc_id = module.vpc.vpc_attributes.id
      vpc_cidr = module.vpc.vpc_attributes.cidr_block
    
      private_subnet_route_table_ids = [for _, value in module.vpc.rt_attributes_by_type_by_az.private : value.id]
      private_subnet_config          = flatten([for _, value in module.vpc.private_subnet_attributes_by_az : {subnet_id = value.id, availability_zone = value.availability_zone}])
    
      kms_key_id = aws_kms_key.rdscfo_kms_key.arn
    
      create_db_subnet_group = true # Toggle to create or assign db subnet group. Defaut name and description will be used.
      create_vpc_endpoints   = true # Toggle to create or not the VPC endpoints. Defaut name and description will be used.
    
      create_iam_role             = true # Toggle to create or assign IAM role. Defaut name and description will be used.
      create_iam_instance_profile = true # Toggle to create or assign IAM instance profile. Defaut name will be used.
    
      aws_db_instance_primary = {
        allocated_storage       = 50
        backup_retention_period = 1
        db_name                 = "ORCL"
        engine                  = data.aws_rds_orderable_db_instance.custom-oracle.engine
        engine_version          = data.aws_rds_orderable_db_instance.custom-oracle.engine_version
        identifier              = "instance-ha"
        instance_class          = data.aws_rds_orderable_db_instance.custom-oracle.instance_class
        username                = "dbadmin"
        password                = "avoid-plaintext-passwords" # Use a password that meets your organizations security policy.
        skip_final_snapshot = true
      }
    
      tags = {
        CostCenter = "example-inc:cost-allocation:CostCenter"
      }
    
      # explict dependency on the VPC module, as module.vpc.aws_route_table_association.private[*] are required for full lifecycle operations
      depends_on = [module.vpc]
    }
    
    output "aws_db_instance_primary_arn" {
      description = "Created RDS primary instance arn."
      value       = module.rds_custom_for_oracle.aws_db_instance_primary_attributes.arn
    }
    
    output "db_subnet_group" {
      description = "Created RDS DB subnet group."
      value       = module.rds_custom_for_oracle.db_subnet_group
    }
  3. Save the file.
  4. In your terminal, initialize Terraform again to install the CfO module:
    terraform init
  5. Preview the changes that Terraform intends to make:
    terraform plan
  6. Apply the Terraform configuration to create the RDS CfO primary instance:
    terraform apply

Create two RDS Custom for Oracle read replicas

Now you extend the configuration of the CfO module from the previous section to include two read replicas. The CfO module automatically geo-distributes the replicas into separate Availability Zones. You can add the replica configuration using the aws_db_instance_replicas nested block attribute.

  1. Replace the definition of the rds_custom_for_oracle in main.tf with the following code (you can change the argument values to your own):
    module "rds_custom_for_oracle" {
      source  = "aws-ia/rds-custom-for-oracle/aws"
      version = ">= 1.0.0"
    
      vpc_id = module.vpc.vpc_attributes.id
      vpc_cidr = module.vpc.vpc_attributes.cidr_block
    
      private_subnet_route_table_ids = [for _, value in module.vpc.rt_attributes_by_type_by_az.private : value.id]
      private_subnet_config          = flatten([for _, value in module.vpc.private_subnet_attributes_by_az : {subnet_id = value.id, availability_zone = value.availability_zone}])
    
      kms_key_id = aws_kms_key.rdscfo_kms_key.arn
    
      create_db_subnet_group = true # Toggle to create or assign db subnet group. Defaut name and description will be used.
      create_vpc_endpoints   = true # Toggle to create or not the VPC endpoints. Defaut name and description will be used.
    
      create_iam_role             = true # Toggle to create or assign IAM role. Defaut name and description will be used.
      create_iam_instance_profile = true # Toggle to create or assign IAM instance profile. Defaut name will be used.
    
      aws_db_instance_primary = {
        allocated_storage       = 50
        backup_retention_period = 1
        db_name                 = "ORCL"
        engine                  = data.aws_rds_orderable_db_instance.custom-oracle.engine
        engine_version          = data.aws_rds_orderable_db_instance.custom-oracle.engine_version
        identifier              = "instance-ha"
        instance_class          = data.aws_rds_orderable_db_instance.custom-oracle.instance_class
        username                = "dbadmin"
        password                = "avoid-plaintext-passwords" # Use a password that meets your organizations security policy.
        skip_final_snapshot = true
      }
    
      aws_db_instance_replicas = {
        replica_count           = 2
        backup_retention_period = 30
        instance_class          = data.aws_rds_orderable_db_instance.custom-oracle.instance_class
        skip_final_snapshot = true
      }
    
      tags = {
        CostCenter = "example-inc:cost-allocation:CostCenter"
      }
    
      # explict dependency on the VPC module, as module.vpc.aws_route_table_association.private[*] are required for full lifecycle operations
      depends_on = [module.vpc]
    }
  2. Preview the changes that Terraform intends to make:
    terraform plan
  3. Apply the Terraform configuration to create the RDS CfO replicas:
    terraform apply

Clean up

When you no longer need the resources created as part of this post, clean up those resources to save associated costs. You can clean up the AWS resources created in this post using Terraform by using the terraform destroy command.

terraform destroy

This will attempt to destroy the CMK created in the first section, but will likely fail because that CMK is associated with a CEV that was created manually using the console. If the terraform destroy command errors, use the console to delete the CEV, and then run the command again.

Conclusion

In this post, we demonstrated how to deploy RDS Custom for Oracle primary and replica instances using Terraform modules. This also involved creating necessary resources like a KMS key, CEV, and VPC.

We recommend testing the modules and given examples in your development environment and making appropriate changes as required to use them in production.

You can find additional Terraform examples on our GitHub repo.

We welcome your feedback. If you have questions or suggestions, leave them in the comments section.


About the Authors

Vishal Patil is a Senior Database Specialist Solutions Architect with Amazon Web Services. Vishal helps customers design architecture, migrate, and optimize Oracle workloads in AWS.

Wesley Hong is a Software Development Manager on the RDS Commercial Engines team.

Tyler Lynch is a Principal Solution Architect at AWS. Tyler supports and contribute to open source software. He is currently in the top 10 contributors of the AWS provider for Terraform, and a Core Contributor for Terraform.