'ValidationException: Before you can proceed, you must enable a service-linked role to give Amazon ES permissions to access your VPC

I am trying to create a VPC controlled Elastic Search Service on AWS. The problem is I keep getting the error when I run the following code: 'ValidationException: Before you can proceed, you must enable a service-linked role to give Amazon ES permissions to access your VPC'.

const AWS = require('aws-sdk');
AWS.config.update({region:'<aws-datacenter>'});
const accessPolicies = {
  Statement: [{
    Effect: "Allow",
    Principal: {
      AWS: "*"
    },
    Action: "es:*",
    Resource: "arn:aws:es:<dc>:<accountid>:domain/<domain-name/*"
  }]
};
const params = {
  DomainName: '<domain>',
  /* required */
  AccessPolicies: JSON.stringify(accessPolicies),
  AdvancedOptions: {
    EBSEnabled: "true",
    VolumeType: "io1",
    VolumeSize: "100",
    Iops: "1000"
  },
  EBSOptions: {
    EBSEnabled: true,
    Iops: 1000,
    VolumeSize: 100,
    VolumeType: "io1"
  },
  ElasticsearchClusterConfig: {
    DedicatedMasterCount: 3,
    DedicatedMasterEnabled: true,
    DedicatedMasterType: "m4.large.elasticsearch",
    InstanceCount: 2,
    InstanceType: 'm4.xlarge.elasticsearch',
    ZoneAwarenessEnabled: true
  },
  ElasticsearchVersion: '5.5',
  SnapshotOptions: {
    AutomatedSnapshotStartHour: 3
  },
  VPCOptions: {
    SubnetIds: [
      '<redacted>',
      '<redacted>'
    ],
    SecurityGroupIds: [
      '<redacted>'
    ]
  }
};

const es = new AWS.ES();
es.createElasticsearchDomain(params, function (err, data) {
  if (err) {
    console.log(err, err.stack); // an error occurred
  } else {
    console.log(JSON.stringify(data, null, 4)); // successful response
  }
});

The problem is I get this error: ValidationException: Before you can proceed, you must enable a service-linked role to give Amazon ES permissions to access your VPC. I cannot seem to figure out how to create this service linked role for the elastic search service. In the aws.amazon.com IAM console I cannot select that service for a role. I believe it is supposed to be created automatically.

Has anybody ran into this or know the way to fix it?



Solution 1:[1]

The service-linked role can be created using the AWS CLI.

aws iam create-service-linked-role --aws-service-name opensearchservice.amazonaws.com

Previous answer: before the service was renamed, you would do the following:

aws iam create-service-linked-role --aws-service-name es.amazonaws.com

Solution 2:[2]

You can now create a service-linked role in a CloudFormation template, similar to the Terraform answer by @htaccess. See the documentation for the CloudFormation syntax for Service-Linked Roles for more details

YourRoleNameHere:
  Type: 'AWS::IAM::ServiceLinkedRole'
  Properties:
    AWSServiceName: es.amazonaws.com
    Description: 'Role for ES to access resources in my VPC'

Solution 3:[3]

Creating a elasticsearch domain with VPC and using aws-sdk/cloudformation is currently not supported. The elasticsearch service requires a special service linked role to create the network interfaces in the specified VPC. This currently possible using console / cli(@Oscar Barrett's answer below).

However, there is a workaround to get this working and it is described as follows:

  • Create a test elasticsearch domain with VPC access using console.
  • This will create a service linked role named AWSServiceRoleForAmazonElasticsearchService [Note: You can not create the role with specified name manually or through thr console]
  • Once this role is created, use aws-sdk or cloudformation to create elasticsearch domain with VPC.
  • You can delete the test elasticsearch domain later

Update: The more correct way to create the service role is described in @Oscar Barrett's answer. I was thinking to delete my answer; but the other facts about the actual issue is still more relevant, thus keeping my answer here.

Solution 4:[4]

For terraform users who hit this error, you can use the aws_iam_service_linked_role resource to create a service linked role for the ES service:

resource "aws_iam_service_linked_role" "es" {
    aws_service_name = "es.amazonaws.com"
    description      = "Allows Amazon ES to manage AWS resources for a domain on your behalf."
}

This resource was added in Release 1.15.0 (April 18, 2018) of the AWS Provider.

Solution 5:[5]

Do it yourself in CDK:

const serviceLinkedRole = new cdk.CfnResource(this, "es-service-linked-role", {
  type: "AWS::IAM::ServiceLinkedRole",
  properties: {
    AWSServiceName: "es.amazonaws.com",
    Description: "Role for ES to access resources in my VPC"
  }
});

const esDomain = new es.CfnDomain(this, "es", { ... });

esDomain.node.addDependency(serviceLinkedRole);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 tryinHard
Solution 3
Solution 4
Solution 5 Otto