Setup inCloud Managed with AWS

inCloud Managed is one of our setup options, which install our platform's infrastructure in a cloud environment owned by your organization, allowing you to delegate its entire setup, update, and maintenance to groundcover.

Note: inCloud Managed is available exclusively to Enterprise users.

inCloud Managed requires to create an isolated account within your AWS organization, that will be managed by groundcover's control plane and will establish, configure, and maintain the infrastructure and workloads within the account. These include AWS VPC, S3, EKS, LB, etc.

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

inCloud Managed deployment (delegated to groundcover)

Sensor deployment (handled by your organization)

Choose a 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.

Choose your telemetry delivery method

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

Prefer private networking is supported with Private Link.

Read more about this option in our Private Network Guide.

Create a cross-account IAM role (full guide)

To grant our control-plane access to the account, we use AWS’s built-in access federation feature. The following guide will guide you through the steps required to setup this access.

Step 1 - Get your External ID

Users on an Enterprise plan (prerequisite for inCloud Managed) have access to a private support channel on Slack for their organization. Use that channel to let us know that you're ready to get started with the inCloud Managed setup. Then, the groundcover team will share your External ID with you.

Step 2: Create a Role

  1. Log in as a privileged user to the AWS account chosen for deployment

  2. Navigate to IAM -> Roles

  3. Click “Create role

Step 3 - Select trusted entity

  1. Trusted entity type: Select “AWS Account

  2. An AWS account: Check "Another AWS account"; Account ID: 991078109329

  3. Options: Check "Require external ID" and enter your External ID (see Step 1)

  4. Make sure “Require MFA” is unchecked

  5. Click "Next"

  6. In the following screen, do not attach any managed permissions

  7. Click "Next"

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

Step 4 - Name, review, and create

In this screen, fill in the following details:

  • Role name: Type groundcover-managed

  • Description: Optional field, your can write any free text description that can be helpful for you and others to understand this purpose of this role in the future (e.g. “app.groundcover.com control plane access”)

  • Tags: Type groundcover:access = read. You can also use specify additional workflow-oriented tags

Click "Create role"

Step 5 - Trust relationships

Search for the newly created groundcover-managed role (or scroll down the list until you find it) and click on it.

Navigate to the "Trust relationships" screen.

Click "Edit trust policy"

Replace the statement with the following snippet:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::991078109329:role/control-plane"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "<External ID: Enter your ID (shared with you by groundcover)>"
        }
      }
    }
  ]
}

Click "Update Policy"

Step 6 - Inline Policy

Navigate to "Permissions" tab.

Click "Add permissions" -> "Create inline policy"

Click the “JSON” tab and paste the following policy into the text box:

{
  "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:Tag*",
        "iam:Untag*",
        "iam:Update*"
      ],
      "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*"]
    }
  ]
}

Click "Next"

Click “Review Policy

Enter the following Policy name: inline-policy-2.4.2

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. Take note of your ARN number, which you will need to share with us (see What's Next?)

Example:

What's next?

Share the ARN and region with us

Using the same Slack channel as in Step 1, share the following information with the groundcover team:

Redeploy the eBPF sensor

Once up and running, we will share our instructions on how to redeploy the eBPF sensor, which will now point the data collected into a public endpoint we will provide for your inCloud. You will get the incloud-values.yaml file from us, which you can use to deploy our sensor on your production workload as follows:

groundcover deploy -f incloud-values.yaml

See API Key Secret for additional information.

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