Virtual Appliance Console Setup

Utilizing the AWS Web Console to deploy a Virtual Appliance

This article documents the best-practice recommendations for setting up your own GRAX Virtual Appliance.

Prerequisites for the Virtual Appliance

To deploy the appliance you will need familiarity with:

  • AWS Organizations to provision an isolated AWS account
  • Route 53 to purchase a domain name and to configure a hosted zone
  • IAM to create policies for the setup and management roles
  • CloudFormation to configure and automatically provision the system

Before you begin to provision the GRAX Virtual Appliance, check that you have the following resources in place, provisioned, or acquired.

  • You will require a GRAX Basic, Pro or Enterprise license that covers the Salesforce instance you wish to backup.
  • Access to either the AWS Console or CLI to with the appropriate IAM permissions to create the resources described in this article.
  • You will need a domain name and DNS Hosted Zone. This is used for provisioning the SSL certificate and configuration of the load balancer.
  • IAM Resources that GRAX requires to provision and operate the service within you AWS Account.

NOTE: We strongly recommend you create an isolated AWS Account for the GRAX service to operate in.

Create the IAM Resources

Our first step is to create the necessary IAM resources for GRAX. When creating these resources we recommend you are familiar with the IAM Best Practices.

There are two distinct IAM Policies shown below; one is required at the initial configuration which necessitates access to create, modify, and/or delete all resources/services utilized within the appliance. This is our Setup Policy. The second policy - our Management Policy - is a minimal log-access focused policy; we require that this policy/access be assigned for the lifetime of the GRAX Virtual Appliance deployment.

Both of these Policies are the “least permissive” set of permissions required by GRAX during setup and continuing operation of the GRAX Virtual Appliance. We recommend that the Setup Policy be enabled immediately prior to installation and disabled immediately after installation is verified.

It is worth nothing that deployment of the GRAX Virtual Appliance does not require use of root IAM privileges, and can be completed by anyone with sufficient access so as to create, modify, and destroy resources for utilized services.

Looking for the CLI Commands?

If you're looking to perform these steps via the AWS CLI, please check out the Virtual Appliance CLI Setup Guide.

Create an Account for GRAX

This is an optional, but recommended step that helps with management and security of your AWS infrastructure.

Create an AWS Account that you wish to provide GRAX access to. We recommend that this account be separated from the rest of your AWS network simply to provide your team with a clear demarcation between GRAX resources and other parts of your infrastructure. The AWS feature that can enable this for you is called AWS Organizations and the configuration of this is beyond the scope of this document. If this is your first time with the Organizations feature, this blog post would be a great starting point to get up to speed.

Setup Policy

GRAX utilizes this policy to provision and configure all services/resources needed to operate an appliance. This policy is a minimally scoped set of permissions which meet these needs. Once GRAX is deployed, this policy can be unassigned and/or deleted.

Create the Policy

Open your IAM Console and select policies. Then click the Create Policy button.

Copy this into the JSON editor.

Data Access Restricted

This policy is very strict about not allowing read access to data that GRAX may contain. Customer data in GRAX will be stored in the S3 bucket and indexed in the Elasticsearch service; you will see this policy does not contain the necessary permissions for accessing this data.

{
  "Statement": [
    {
      "Action": [
        "acm:AddTagsToCertificate",
        "acm:DeleteCertificate",
        "acm:DescribeCertificate",
        "acm:RequestCertificate",
        "autoscaling:CreateAutoScalingGroup",
        "autoscaling:CreateLaunchConfiguration",
        "autoscaling:DeleteAutoScalingGroup",
        "autoscaling:DeleteLaunchConfiguration",
        "autoscaling:DescribeAutoScalingGroups",
        "autoscaling:DescribeAutoScalingInstances",
        "autoscaling:DescribeLaunchConfigurations",
        "autoscaling:DescribeScalingActivities",
        "autoscaling:UpdateAutoScalingGroup",
        "cloudformation:CreateChangeSet",
        "cloudformation:CreateStack",
        "cloudformation:DeleteStack",
        "cloudformation:DescribeChangeSet",
        "cloudformation:DescribeStackEvents",
        "cloudformation:DescribeStackResource",
        "cloudformation:DescribeStackResources",
        "cloudformation:DescribeStacks",
        "cloudformation:EstimateTemplateCost",
        "cloudformation:GetStackPolicy",
        "cloudformation:GetTemplateSummary",
        "cloudformation:ListStackResources",
        "cloudformation:ListStacks",
        "cloudformation:SignalResource",
        "cloudformation:UpdateStack",
        "cloudformation:UpdateStackSet",
        "ec2:AllocateAddress",
        "ec2:AssociateRouteTable",
        "ec2:AttachInternetGateway",
        "ec2:AuthorizeSecurityGroupIngress",
        "ec2:CreateInternetGateway",
        "ec2:CreateNatGateway",
        "ec2:CreateNetworkInterface",
        "ec2:CreateRoute",
        "ec2:CreateRouteTable",
        "ec2:CreateSecurityGroup",
        "ec2:CreateSubnet",
        "ec2:CreateTags",
        "ec2:CreateVpc",
        "ec2:CreateVpcEndpoint",
        "ec2:DeleteInternetGateway",
        "ec2:DeleteNatGateway",
        "ec2:DeleteNetworkInterface",
        "ec2:DeleteRoute",
        "ec2:DeleteRouteTable",
        "ec2:DeleteSecurityGroup",
        "ec2:DeleteSubnet",
        "ec2:DeleteVpc",
        "ec2:DeleteVpcEndpoints",
        "ec2:DescribeAccountAttributes",
        "ec2:DescribeAddresses",
        "ec2:DescribeAvailabilityZones",
        "ec2:DescribeImages",
        "ec2:DescribeInstanceAttribute",
        "ec2:DescribeInstanceStatus",
        "ec2:DescribeInstances",
        "ec2:DescribeInternetGateways",
        "ec2:DescribeNatGateways",
        "ec2:DescribeRouteTables",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeSubnets",
        "ec2:DescribeTags",
        "ec2:DescribeVpcEndpoints",
        "ec2:DescribeVpcs",
        "ec2:DetachInternetGateway",
        "ec2:DisassociateRouteTable",
        "ec2:ModifySubnetAttribute",
        "ec2:ModifyVpcAttribute",
        "ec2:ReleaseAddress",
        "ec2:RunInstances",
        "ec2:TerminateInstances",
        "elasticloadbalancing:AddListenerCertificates",
        "elasticloadbalancing:AddTags",
        "elasticloadbalancing:CreateListener",
        "elasticloadbalancing:CreateLoadBalancer",
        "elasticloadbalancing:CreateTargetGroup",
        "elasticloadbalancing:DeleteListener",
        "elasticloadbalancing:DeleteLoadBalancer",
        "elasticloadbalancing:DeleteTargetGroup",
        "elasticloadbalancing:DeregisterTargets",
        "elasticloadbalancing:DescribeListeners",
        "elasticloadbalancing:DescribeLoadBalancers",
        "elasticloadbalancing:DescribeTargetGroupAttributes",
        "elasticloadbalancing:DescribeTargetGroups",
        "elasticloadbalancing:DescribeTargetHealth",
        "elasticloadbalancing:ModifyTargetGroupAttributes",
        "elasticloadbalancing:RemoveListenerCertificates",
        "elasticloadbalancing:SetSecurityGroups",
        "elasticloadbalancing:SetWebAcl",
        "es:AddTags",
        "es:CreateElasticsearchDomain",
        "es:DeleteElasticsearchDomain",
        "es:DescribeElasticsearchDomain",
        "es:UpdateElasticsearchDomainConfig",
        "iam:AddRoleToInstanceProfile",
        "iam:AttachRolePolicy",
        "iam:CreateInstanceProfile",
        "iam:CreateRole",
        "iam:CreateServiceLinkedRole",
        "iam:DeleteInstanceProfile",
        "iam:DeleteRole",
        "iam:DeleteRolePolicy",
        "iam:DetachRolePolicy",
        "iam:GetRole",
        "iam:GetRolePolicy",
        "iam:ListRoles",
        "iam:PassRole",
        "iam:PutRolePolicy",
        "iam:RemoveRoleFromInstanceProfile",
        "iam:TagInstanceProfile",
        "iam:TagRole",
        "kms:CreateKey",
        "kms:DescribeKey",
        "kms:GetKeyPolicy",
        "kms:GetKeyRotationStatus",
        "kms:ListResourceTags",
        "kms:PutKeyPolicy",
        "kms:ScheduleKeyDeletion",
        "kms:TagResource",
        "logs:CreateLogGroup",
        "logs:DeleteLogGroup",
        "logs:DescribeLogGroups",
        "logs:DescribeLogStreams",
        "logs:DescribeQueryDefinitions",
        "logs:DescribeQueries",
        "logs:FilterLogEvents",
        "logs:GetLogEvents",
        "logs:GetQueryResults",
        "logs:StartQuery",
        "logs:StopQuery",
        "rds:AddTagsToResource",
        "rds:CreateDBCluster",
        "rds:CreateDBClusterSnapshot",
        "rds:CreateDBInstance",
        "rds:CreateDBSubnetGroup",
        "rds:DeleteDBCluster",
        "rds:DeleteDBInstance",
        "rds:DeleteDBSubnetGroup",
        "rds:DescribeDBClusterSnapshots",
        "rds:DescribeDBClusters",
        "rds:DescribeDBInstances",
        "rds:DescribeDBSubnetGroups",
        "rds:ModifyDBCluster",
        "route53:ChangeResourceRecordSets",
        "route53:GetChange",
        "route53:GetHostedZone",
        "route53:ListHostedZones",
        "route53:ListResourceRecordSets",
        "s3:CreateBucket",
        "s3:GetBucketLocation",
        "s3:PutBucketPublicAccessBlock",
        "s3:PutBucketTagging",
        "s3:PutEncryptionConfiguration",
        "secretsmanager:CreateSecret",
        "secretsmanager:DeleteSecret",
        "secretsmanager:GetRandomPassword",
        "secretsmanager:GetSecretValue",
        "secretsmanager:PutSecretValue",
        "secretsmanager:TagResource",
        "secretsmanager:UpdateSecret",
        "ssm:GetParameters",
        "ssm:StartSession",
        "wafv2:AssociateWebACL",
        "wafv2:CreateIPSet",
        "wafv2:CreateRegexPatternSet",
        "wafv2:CreateWebACL",
        "wafv2:DeleteIPSet",
        "wafv2:DeleteRegexPatternSet",
        "wafv2:DeleteWebACL",
        "wafv2:DisassociateWebACL",
        "wafv2:GetIPSet",
        "wafv2:GetRegexPatternSet",
        "wafv2:GetWebACL",
        "wafv2:GetWebACLForResource",
        "wafv2:ListTagsForResource",
        "wafv2:UpdateIPSet",
        "wafv2:UpdateWebACL"
      ],
      "Effect": "Allow",
      "Resource": "*",
      "Sid": "GraxSetup"
    }
  ],
  "Version": "2012-10-17"
}

Click Next: Tags.

Add Tags

Add any tags to the Policy. This is not necessary but Tags are quite useful when tracking different resources created for GRAX.

Review and Name

Name the Policy something meaningful to your organization (we chose "GraxSetup") and then click Create Policy.

Screen Shot 2021-06-23 at 3.17.17 pm.pngScreen Shot 2021-06-23 at 3.17.17 pm.png

Management Policy

GRAX requires that this policy be in place during the entire lifetime of GRAX in your AWS account. This policy allows us to monitor logs and inspect the health and configuration of stacks. This policy does not allow access to any data storage resources or the application instance itself. GRAX application logs are sterilized and do not contain customer data or secrets.

Create the Policy

Go back to the IAM Home or open your IAM Console and select policies. Then click the Create Policy button.

Review, then copy and paste the policy JSON below into the JSON editor to grant the access that is required.

Policy JSON
{
  "Statement": [
    {
      "Action": [
        "autoscaling:CreateAutoScalingGroup",
        "autoscaling:DeleteAutoScalingGroup",
        "autoscaling:DescribeAutoScalingGroups",
        "autoscaling:DescribeAutoScalingInstances",
        "autoscaling:DescribeLaunchConfigurations",
        "autoscaling:DescribeScalingActivities",
        "autoscaling:UpdateAutoScalingGroup",
        "cloudformation:CreateChangeSet",
        "cloudformation:DescribeChangeSet",
        "cloudformation:DescribeStackEvents",
        "cloudformation:DescribeStackResource",
        "cloudformation:DescribeStackResources",
        "cloudformation:DescribeStacks",
        "cloudformation:EstimateTemplateCost",
        "cloudformation:GetStackPolicy",
        "cloudformation:GetTemplateSummary",
        "cloudformation:ListStackResources",
        "cloudformation:ListStacks",
        "cloudformation:SignalResource",
        "cloudformation:UpdateStack",
        "cloudformation:UpdateStackSet",
        "cloudwatch:GetMetricStatistics",
        "cloudwatch:ListMetrics",
        "ec2:DescribeImages",
        "ec2:DescribeInstances",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeSubnets",
        "ec2:DescribeVpcs",
        "es:DescribeElasticsearchDomain",
        "iam:GetRole",
        "iam:GetRolePolicy",
        "iam:ListRoles",
        "iam:PassRole",
        "logs:DescribeLogGroups",
        "logs:DescribeLogStreams",
        "logs:DescribeQueryDefinitions",
        "logs:DescribeQueries",
        "logs:FilterLogEvents",
        "logs:GetLogEvents",
        "logs:GetQueryResults",
        "logs:StartQuery",
        "logs:StopQuery",
        "ssm:GetParameters"
      ],
      "Effect": "Allow",
      "Resource": "*",
      "Sid": "GraxManagement"
    }
  ],
  "Version": "2012-10-17"
}

Add Tags

Add any tags to the Policy. This is not necessary but Tags are quite useful when tracking different resources created for GRAX.

Review

Review and create the policy.

Screen Shot 2021-06-23 at 3.24.59 pm.pngScreen Shot 2021-06-23 at 3.24.59 pm.png

Interactive Sessions Policy

When a support case or other issue requires interactive access to the instance running the GRAX application, we will request the GraxSessions policy be added to the role or user connected to our AWS access.

Create the Policy

Go back to the IAM Home or open your IAM Console and select policies. Then click the Create Policy button.

Review, then copy and paste the policy JSON below into the JSON editor to grant the access that is required.

Policy JSON
{
  "Statement": [
    {
      "Action": [
        "ssm:StartSession"
      ],
      "Effect": "Allow",
      "Resource": "*",
      "Sid": "GraxSessions"
    }
  ],
  "Version": "2012-10-17"
}

Add Tags

Add any tags to the Policy. This is not necessary but Tags are quite useful when tracking different resources created for GRAX.

Review

Review and create the policy.

Grant Access to GRAX

AWS IAM provides the ability to setup a cross account trust relationship that will provide GRAX the access required to create, manage, and destroy resources in this account. This pattern of providing third-party access to your AWS account is documented in the IAM User Guide.

GRAX utilizes IAM Assume Role to utilize the permissions provided via the above policies. This role specifically grants trust only through the GRAX root account and requires a unique external ID value upon utilization. Access via this role is auditable at any time via the IAM utilities in your AWS account.

GRAX support will supply your external ID during provisioning of your GRAX licenses. This is referred to as EXTERNAL_ID in these docs and will be a UUID, e.g. AEF80FCA-C184-48FA-B432-61B948D0259.

If your team wishes to understand the exact nature of what is deployed, we can share the CloudFormation scripts under an NDA. Contact GRAX for more information. The AWS Deployment page goes into some detail on the components of GRAX.

There are two steps that are required.

Create an IAM Role

From your AWS Console, open the IAM dashboard and create a role.

AWS Create Role.pngAWS Create Role.png

Select the trusted entity as Another AWS Account and enter the GRAX Account ID 956140928604, and the External ID provided to you by GRAX support.

Or, click to create this role in your IAM Console.

Screen Shot 2021-06-23 at 3.28.44 pm.pngScreen Shot 2021-06-23 at 3.28.44 pm.png

Search for the Grax Setup policy created in the above step. Later you can transition to the GRAX Management Policy.

Screen Shot 2021-06-23 at 3.27.25 pm.pngScreen Shot 2021-06-23 at 3.27.25 pm.png

Select this role and click Next: Tags.

Add any tags that you might need and click Next: Review.

Give this role a name and description and click Create Role.

Screen Shot 2021-03-03 at 12.03.41 pm.pngScreen Shot 2021-03-03 at 12.03.41 pm.png

Send the Created ARN to GRAX

Once you have created the role you should be able to see the Role ARN. Copy this and send to your GRAX contact.

Role Summary - ARN.pngRole Summary - ARN.png

Create the Infrastructure for GRAX

GRAX Can Do This For You!

The rest of this documentation is going to describe the steps you would take to create the GRAX environment yourself. If you have granted the GRAX Team access via the above Management Role, then you don't need to worry about this.

Standard VPC Setup

The standard configuration of GRAX creates a VPC with 3 subnets, 2 NAT gateways and an Internet Gateway. Create this using the reference CloudFormation template vpc-standard.yml (you can request access from your GRAX representative).

Navigate to the CloudFormation console and click Create stack.

Screen Shot 2021-04-07 at 12.59.41 pm.pngScreen Shot 2021-04-07 at 12.59.41 pm.png

Upload the template file and click Next.

Screen Shot 2021-04-07 at 1.01.11 pm.pngScreen Shot 2021-04-07 at 1.01.11 pm.png

Name the stack that is about to be created. You can specify the CIDR blocks for the subnets, or just accept the defaults in the template.

Screen Shot 2021-04-07 at 1.03.33 pm.pngScreen Shot 2021-04-07 at 1.03.33 pm.png

Define the tags and configure permissions and advanced options (you can accept the defaults here).

Screen Shot 2021-04-07 at 1.14.04 pm.pngScreen Shot 2021-04-07 at 1.14.04 pm.png

Review your Stack Create options and then create the stack. Go grab a drink and check back in a few minutes.

HTTPS Load Balancer Setup

This is where we create the load balancer that is going to accept the requests to the GRAX Virtual Appliance; these requests will come from any Salesforce instance that is configured as well as the GRAX Control Plane.

This step involves some pre-requisites, namely the DNS and SSL Certificate. Normally, your organisation will have internal processes around this, but we will document the necessary steps for creation from scratch here for completeness.

Create a New Domain and DNS Hosted Zone

Open the Route 53 console and register a new domain.

WARNING: An Example Only

Creation of a Domain, DNS Hosted Zone and SSL Certificate can be managed by different services in your organisation. Consult your network and security engineers before creating your own using the AWS services listed here.

Check that your domain is available and purchase it. This should now appear in your Registered Domains dashboard.

Screen Shot 2021-04-07 at 1.39.40 pm.pngScreen Shot 2021-04-07 at 1.39.40 pm.png

You next need to create the Hosted Zone for the DNS entries that manage this domain. This will need to be a public hosted zone. Add any tags required here as well.

Screen Shot 2021-04-07 at 1.42.30 pm.pngScreen Shot 2021-04-07 at 1.42.30 pm.png

Once you have the Domain and DNS Hosted Zone we can continue to create the Load Balancer.

Create the Load Balancer

The load balancer template will require the following parameters.

DomainName

This is going to be the fully qualified name that is the endpoint for GRAX. When we create these on behalf of customers we typically follow the convention of environment.region.subdomain
In this example then we would end up with:

DomainName = production.us-east-1.myappliance.com

HostedZoneId

This is the DNS Hosted zone created above. The ID value is available in the Hosted Zone details.

Screen Shot 2021-04-07 at 2.03.00 pm.pngScreen Shot 2021-04-07 at 2.03.00 pm.png

HostedZoneId = Z07425741A9E1YILJTDVV

Subnet and VPC Params

Subnet values are outputs of the CloudFormation script we ran earlier to create the VPC. Navigate to the CloudFormation dashboard and find your stack, then select the Outputs tab.

Screen Shot 2021-04-07 at 2.06.42 pm.pngScreen Shot 2021-04-07 at 2.06.42 pm.png

Tier1Subnet0 = subnet-0a093aeed915586a0
Tier1Subnet1 = subnet-0ef2ead9cfcf09b5c
VPC = vpc-0795a76b9993356e9

Now, upload the next CloudFormation template alb-dns.yml.

Screen Shot 2021-04-07 at 2.06.42 pm.pngScreen Shot 2021-04-07 at 2.06.42 pm.png

Populate the parameters that we noted above.

Screen Shot 2021-04-07 at 2.14.05 pm.pngScreen Shot 2021-04-07 at 2.14.05 pm.png

Configure tags and options.

Screen Shot 2021-04-07 at 2.15.12 pm.pngScreen Shot 2021-04-07 at 2.15.12 pm.png

Review and create the stack. Fetch yourself another drink while this creates.

Firewall Setup (Optional)

Some of our customers like to have a Web Application Firewall attached to the Load Balancer to limit connections only from Salesforce and the GRAX Control Plane. If this doesn't describe your company then feel free to skip to the Runtime Setup.

As you are probably used to by now, we are going to use CloudFormation to create the firewall. This waf.yml template configures the AWS WAF V2 onto the load balancer created above, so you will need to get the Load Balancer ARN as a parameter for this step.

Screen Shot 2021-04-07 at 2.29.29 pm.pngScreen Shot 2021-04-07 at 2.29.29 pm.png

From the CloudFormation dashboard, create a new stack and upload the template for the firewall. Enter a name and the Load Balancer ARN.

Screen Shot 2021-04-07 at 2.31.52 pm.pngScreen Shot 2021-04-07 at 2.31.52 pm.png

Add tags and any further configuration.

Screen Shot 2021-04-07 at 2.33.12 pm.pngScreen Shot 2021-04-07 at 2.33.12 pm.png

Review, click Create Stack and fetch another coffee.

Runtime Setup

You are now ready to create the runtime. Our last Cloudformation runtime.yml template ties all this together. As such, it will require parameters from all of our previous steps. These can be found as outputs in each of the relevant stacks.

For our example, this is the list of parameters we are going to be using.

DomainName (output of  Load Balancer) template = prod.us-east-1.graxdev-tsellers.com 
LoadBalancerSecurityGroup (output of Load Balancer template) = sg-0728fea8e4d0f4a3f
TargetGroup (output of Load Balancer template) = arn:aws:elasticloadbalancing:us-east-1:888271713940:targetgroup/grax-Targe-IJRJC93GPTNK/56a0b1a6c19f5423
Tier1Subnet0 (output of VPC template) = subnet-0a093aeed915586a0
Tier1Subnet1 (output of VPC template) = subnet-0ef2ead9cfcf09b5c
Tier2Subnet0 (output of VPC template) = subnet-027122f8bf7f7d8ce
Tier2Subnet1 (output of VPC template) = subnet-01a54d2f387a2d651
Tier3Subnet0 (output of VPC template) = subnet-0f4b6c183f198c054
Tier3Subnet1 (output of VPC template) = subnet-06fa258b3c33c98b5
VPC (output of VPC template) = vpc-0795a76b9993356e9

One last time, create a stack from your CloudFormation dashboard and load the runtime.yml template.

This time you will notice there are many more parameters than what is listed here. The components documentation makes recommendations, but this template comes with defaults that should satisfy most use cases.

Screen Shot 2021-04-07 at 2.42.30 pm.pngScreen Shot 2021-04-07 at 2.42.30 pm.png

Add the Tags and further configurations as required, review and create. On the review step this time you will notice that this template will create some new IAM capabilities as well.

Screen Shot 2021-04-07 at 2.42.30 pm.pngScreen Shot 2021-04-07 at 2.42.30 pm.png

Get some more coffee (you might have had enough coffee for now).

Configure Salesforce

Once your Virtual Appliance has been configured and started, you can connect your Salesforce org and configure your first backup. If you are yet to do so, Install the GRAX Managed Package to your Salesforce org.

Make sure you have located the Engagementgraph Secret from in Secrets Manager and take note of these tokens. You will need this and the API URL for configuring the Salesforce Managed Package. The values you will need from your secret are:

  • api_url
  • gateway_token
  • api_token
  • license_token

Screen Shot 2021-04-07 at 4.59.08 pm.pngScreen Shot 2021-04-07 at 4.59.08 pm.png

Once you have completed your setup, you should be able to see the log output in your Cloudwatch dashboard. The runtime.yml CloudFormation template contains a log group where you will be able to monitor your log stream.