Setup Managed inCloud with AWS

Note: groundcover inCloud is available only to users subscribed to one of our paid plans.

groundcover Managed inCloud is a managed enterprise solution for installing groundcover’s observability infrastructure in a cloud environment owned by your organization.

To set up groundcover Managed inCloud, you need to create an isolated account [groundcover-incloud] within your AWS Organization. This account will be managed by groundcover's control plane that will establish, configure, and maintain the infrastructure and workloads within the account. These include AWS VPC, S3, EKS, and NLB services.

The following sequence diagram provides an overview of the setup process:

Overview of inCloud deployment (managed)

Sensor deployment (handled by your SRE team)

Setup Guide

Chapter 1: Choosing method of deployment

groundcover Managed inCloud can be deployed using one the following configurations:

Option A: Creating a dedicated observability sub-account

In line with AWS’s recommended unit of containerization, the default option is to deploy in a dedicated AWS account. This acts as an identity, resources, quota and access management isolation boundary.

We recommend naming the account [groundcover-incloud] and placing the account in OU=Infrastructure/OU=managed. For additional information please see Establishing your best practice AWS environment (external link to a page on the AWS website).

Option B: Deploying into an existing AWS account

For customers who prefer using a single account approach, Managed inCloud can also be deployed into an existing account, running alongside existing production workloads in your existing AWS account. To limit access and prevent resource collusion, we implement a “scoping territory” approach using ABAC tags for access control and VPC subnets for network control.

Chapter 2: Choosing your telemetry delivery method

By default, groundcover Managed inCloud deploys as a SaaS solution using ZTNA public IPs, allowing you to deliver telemetry data securely over the public internet.

If you choose to use private networking, you can also deploy into a pre-existing, peered VPC.

Read more about this option in our Private Network Guide.

Chapter 3: Create a cross-account IAM role

To grant our control-plane access to the account, we use AWS’s built-in access federation (external link to a page on the AWS website) feature. The following guide will guide you through the steps required to setup this access.

Step 1 - Create a Role

  1. While logged in as a privileged user to the AWS account (i.e. the one chosen for deployment in Chapter 1, Option A or Option B), go to IAM > Roles.

  2. Click “Create role” within the AWS console.

Step 2 - Select trusted entity

  1. In the “Trusted entity type” select “AWS Account

  2. In “Another AWS account” please type in Account ID: 991078109329

  3. In the "External ID" field, enter the value that has been shared with you by your integration manager.

  4. Leave “Required MFA” unchecked.

Note: AWS can’t verify MFA login in cross-account operational mode. groundcover control-plane is enforcing strict security principals, including MFA.

Step 3 - Add permissions

Do not attach any managed permissions.

  1. Click "Next".

Step 4 - Name, review, and create

  1. Role name

    Use groundcover-managed for the role name.

  2. Description

    Optional: Write a helpful description free text (e.g. “app.groundcover.com control plane access”).

  3. Tags groundcover:access = read

    Optional: Specify additional workflows-oriented tags.

Step 5 - Trust relationships

  1. Click the newly created groundcover-managed role

  2. Navigate to the "Trust relationships" screen.

  3. Click "Edit trust policy"

  4. Replace the "Principal" section with the following snippet:

    ...
    			"Principal": {
    				"AWS": "arn:aws:iam::991078109329:role/control-plane"
    			},
    ...
  5. Click "Update Policy"

Step 6 - Inline Policy

  1. Click the newly created groundcover-managed role

  2. Navigate to "Permissions" screen.

  3. Click "Add permissions" > "Create inline policy"

  4. Click the “JSON” tab.

  1. Paste the following policy into the text area:

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "Access",
          "Effect": "Allow",
          "Resource": "*",
          "Action": [
            "sts:GetCallerIdentity",
            "sts:SetSourceIdentity"
          ]
        },
        {
          "Sid": "ConsoleAccess",
          "Effect": "Allow",
          "Resource": "*",
          "Action": [
            "elasticloadbalancing:Describe*",
            "eks:List*"
          ]
        },
        {
          "Sid": "CostController",
          "Effect": "Allow",
          "Resource": "*",
          "Action": [
            "aws-portal:ViewBilling",
            "billing:Get*",
            "billing:List*",
            "ce:Describe*",
            "ce:Get*",
            "ce:List*",
            "cur:Get*",
            "cur:Describe*"
          ]
        },
        {
          "Sid": "HealthController",
          "Effect": "Allow",
          "Resource": "*",
          "Action": [
            "cloudwatch:Describe*",
            "cloudwatch:Get*",
            "cloudwatch:List*",
            "health:Describe*",
            "logs:Describe*",
            "logs:Filter*",
            "logs:List*",
            "logs:Start*",
            "logs:Stop*",
            "logs:Test*",
            "servicequotas:Get*",
            "servicequotas:List*",
            "ec2messages:Get*",
            "events:Describe*",
            "events:List*",
            "events:Test*"
          ]
        },
        {
          "Sid": "HealthControllerLogsGet",
          "Effect": "Allow",
          "Resource": [
            "arn:aws:logs:*:*:log-group:*groundcover*"
          ],
          "Action": [
            "logs:Get*"
          ]
        },
        {
          "Sid": "DriftReconciler",
          "Effect": "Allow",
          "Resource": "*",
          "Action": [
            "iam:ListPolicies",
            "iam:ListRolePolicies",
            "iam:ListInstanceProfilesForRole",
            "iam:ListAttachedRolePolicies",
            "ec2:Describe*",
            "eks:Describe*"
          ]
        },
        {
          "Sid": "DriftReconcilerServiceRoles",
          "Effect": "Allow",
          "Action": [
            "iam:CreateServiceLinkedRole",
            "iam:DeleteServiceLinkedRole",
            "iam:GetServiceLinkedRoleDeletionStatus"
          ],
          "Resource": [
            "arn:aws:iam::*:role/aws-service-role/eks-nodegroup.amazonaws.com/*",
            "arn:aws:iam::*:role/aws-service-role/autoscaling.amazonaws.com/*",
            "arn:aws:iam::*:role/aws-service-role/spot.amazonaws.com/*",
            "arn:aws:iam::*:role/aws-service-role/elasticloadbalancing.amazonaws.com/*"
          ]
        },
        {
          "Sid": "DriftReconcilerGetRole",
          "Effect": "Allow",
          "Action": [
            "iam:GetRole"
          ],
          "Resource": [
            "arn:aws:iam::*:role/*groundcover*",
            "arn:aws:iam::*:role/aws-service-role/eks-nodegroup.amazonaws.com/*"
          ]
        },
        {
          "Sid": "ControlPlaneEC2TagOnCreate",
          "Effect": "Allow",
          "Action": [
            "ec2:CreateTags"
          ],
          "Resource": "*",
          "Condition": {
            "StringLike": {
              "ec2:CreateAction": [
                "Create*"
              ]
            },
            "StringNotEquals": {
              "ec2:CreateAction": [
                "CreateTags"
              ]
            }
          }
        },
        {
          "Sid": "ControlPlaneModifyUntagged",
          "Effect": "Allow",
          "Resource": "*",
          "Action": [
            "ec2:ReplaceRouteTableAssociation",
            "ec2:DisassociateAddress"
          ]
        },
        {
          "Sid": "ControlPlaneCreateTagged",
          "Effect": "Allow",
          "Resource": "*",
          "Action": [
            "acm:RequestCertificate",
            "ec2:AllocateAddress",
            "ec2:Create*",
            "ec2:Run*",
            "eks:CreateCluster",
            "eks:Tag*",
            "logs:Create*",
            "logs:Tag*",
            "iam:CreateRole",
            "iam:CreateOpenIDConnectProvider",
            "iam:Tag*"
          ],
          "Condition": {
            "StringEquals": {
              "aws:RequestTag/groundcover:access": "owner"
            },
            "ForAnyValue:StringEquals": {
              "aws:TagKeys": [
                "groundcover:access"
              ]
            }
          }
        },
        {
          "Sid": "ControlPlaneReadBygroundcoveraccess",
          "Effect": "Allow",
          "Resource": "*",
          "Action": [
            "ec2:Get*",
            "iam:Get*"
          ],
          "Condition": {
            "StringEquals": {
              "aws:ResourceTag/groundcover:access": "read"
            }
          }
        },
        {
          "Sid": "ControlPlaneModifyBygroundcoveraccess",
          "Effect": "Allow",
          "Resource": "*",
          "Action": [
            "acm:*",
            "ec2:*",
            "eks:*",
            "logs:*",
            "elasticloadbalancing:*",
            "iam:AddClientIDToOpenIDConnectProvider",
            "iam:Get*",
            "iam:Delete*",
            "iam:DetachRolePolicy",
            "iam:TagRole",
            "iam:Untag*"
          ],
          "Condition": {
            "StringEquals": {
              "aws:ResourceTag/groundcover:access": "owner"
            }
          }
        },
        {
          "Sid": "ControlPlaneModifyTaggedByeksclusername",
          "Effect": "Allow",
          "Resource": "*",
          "Action": [
            "ec2:*"
          ],
          "Condition": {
            "StringLike": {
              "aws:ResourceTag/eks:cluster-name": "groundcover*"
            }
          }
        },
        {
          "Sid": "ControlPlaneModifyTaggedByawseksclusername",
          "Effect": "Allow",
          "Resource": "*",
          "Action": [
            "ec2:*"
          ],
          "Condition": {
            "StringLike": {
              "aws:ResourceTag/aws:eks:cluster-name": "groundcover*"
            }
          }
        },
        {
          "Sid": "ControlPlaneModifyTaggedByKubernetesCluster",
          "Effect": "Allow",
          "Resource": "*",
          "Action": [
            "ec2:*"
          ],
          "Condition": {
            "StringLike": {
              "aws:ResourceTag/KubernetesCluster": "groundcover*"
            }
          }
        },
        {
          "Sid": "ControlPlaneEKSRoleAttachPolicy",
          "Effect": "Allow",
          "Action": [
            "iam:AttachRolePolicy"
          ],
          "Resource": "arn:aws:iam::*:role/groundcover*",
          "Condition": {
            "ArnEquals": {
              "iam:PolicyARN": [
                "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",
                "arn:aws:iam::aws:policy/AmazonEKSServicePolicy",
                "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy",
                "arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy",
                "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy",
                "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
              ]
            },
            "StringEquals": {
              "aws:ResourceTag/groundcover:access": "owner"
            }
          }
        },
        {
          "Sid": "ControlPlaneEKSPassRole",
          "Effect": "Allow",
          "Action": "iam:PassRole",
          "Resource": "arn:aws:iam::*:role/groundcover*",
          "Condition": {
            "StringEquals": {
              "iam:PassedToService": [
                "eks.amazonaws.com"
              ]
            }
          }
        },
        {
          "Sid": "ControlPlaneEKSRoles",
          "Effect": "Allow",
          "Action": "iam:PutRolePolicy",
          "Resource": [
            "arn:aws:iam::*:role/groundcover*"
          ]
        },
        {
          "Sid": "ControlPlaneS3",
          "Effect": "Allow",
          "Action": "s3:*",
          "Resource": [
            "arn:aws:s3:::groundcover*"
          ]
        }
      ]
    }

  2. Click “Review Policy

  3. Name: inline-policy-2.4.0

  4. Click “Create Policy

We create the inline policy to adhere to security best practices (external link to Wikipedia) by limiting access on IAM, EC2 and related services to the bare minimum required for control plane operations including: health monitoring, security patches, auto scaling and version updates.

Sharing the ARN & External ID

Security of groundcover Control-Plane

groundcover Control-Plane is a secure reconciliation controller designed to manage enterprise inCloud infrastructure environments. It is compliant with ISO-27001 and SOC-2 standards.

The control plane can securely access your groundcover-incloud account by using a cross-account IAM role.

Setting up groundcover inCloud does not require access to your production data or workloads, nor does it grant it such access.

Binding Access

To establish the Trust Relationship, please share the following information with groundcover.

  1. Go to IAM > Roles > groundcover-managed > Trust relationships.

  2. Verify that the sts:ExternalId is as was provided by groundcover's integration manager.

  3. Share the role ARN with your integration manager over a private channel.

Example:

  • ARN: arn:aws:iam::636480266466:role/groundcover-managed

  • sts:ExternalId: 8a1b84011827e7c5586f9d1814283ef7

Reconciliation

At this stage, our automation kicks in. Please allow approximately 2 hours for the initial reconciliation loop to stabilize. Once stabilized, your integration manager will share with you (using a private channel) the incloud-values.yaml that should be used during sensor deployment on production workload, in the following manner:

groundcover deploy -f incloud-values.yaml

Please see API Key Secret for additional information.

Start using groundcover Managed inCloud

You can now log in to app.groundcover.com to use groundcover Managed inCloud, with total data control and privacy.

Data is transmitted to, processed on, and stored in the [groundcover-incloud] managed infrastructure. The mental model is that of a private SaaS solution.


Last updated