Skip to content

Commit

Permalink
Merge pull request #122 from hryamzik/integrations
Browse files Browse the repository at this point in the history
datadog_integration_aws
  • Loading branch information
masci authored Nov 27, 2018
2 parents c20303e + 66fd56b commit 6f35249
Show file tree
Hide file tree
Showing 4 changed files with 298 additions and 0 deletions.
1 change: 1 addition & 0 deletions datadog/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func Provider() terraform.ResourceProvider {
"datadog_screenboard": resourceDatadogScreenboard(),
"datadog_user": resourceDatadogUser(),
"datadog_integration_gcp": resourceDatadogIntegrationGcp(),
"datadog_integration_aws": resourceDatadogIntegrationAws(),
},

ConfigureFunc: providerConfigure,
Expand Down
211 changes: 211 additions & 0 deletions datadog/resource_datadog_integration_aws.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package datadog

import (
"fmt"
"os"
"strings"

"github.com/hashicorp/terraform/helper/schema"
"github.com/zorkian/go-datadog-api"
)

func accountAndRoleFromID(id string) (string, string) {
result := strings.SplitN(id, ":", 2)
return result[0], result[1]
}

func resourceDatadogIntegrationAws() *schema.Resource {
return &schema.Resource{
Create: resourceDatadogIntegrationAwsCreate,
Read: resourceDatadogIntegrationAwsRead,
Update: resourceDatadogIntegrationAwsUpdate,
Delete: resourceDatadogIntegrationAwsDelete,
Exists: resourceDatadogIntegrationAwsExists,
Importer: &schema.ResourceImporter{
State: resourceDatadogIntegrationAwsImport,
},

Schema: map[string]*schema.Schema{
"account_id": {
Type: schema.TypeString,
Required: true,
},
"role_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true, // waits for update API call support
},
"filter_tags": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
ForceNew: true, // waits for update API call support
},
"host_tags": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
ForceNew: true, // waits for update API call support
},
"account_specific_namespace_rules": {
Type: schema.TypeMap,
Optional: true,
Elem: schema.TypeBool,
ForceNew: true, // waits for update API call support
},
"external_id": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func resourceDatadogIntegrationAwsExists(d *schema.ResourceData, meta interface{}) (b bool, e error) {
// Exists - This is called to verify a resource still exists. It is called prior to Read,
// and lowers the burden of Read to be able to assume the resource exists.
client := meta.(*datadog.Client)

integrations, err := client.GetIntegrationAWS()
if err != nil {
return false, err
}
accountID, roleName := accountAndRoleFromID(d.Id())
for _, integration := range *integrations {
if integration.GetAccountID() == accountID && integration.GetRoleName() == roleName {
return true, nil
}
}
return false, nil
}

func resourceDatadogIntegrationAwsPrepareCreateRequest(d *schema.ResourceData, accountID string, roleName string) datadog.IntegrationAWSAccount {

iaws := datadog.IntegrationAWSAccount{
AccountID: datadog.String(accountID),
RoleName: datadog.String(roleName),
}

filterTags := []string{}

if attr, ok := d.GetOk("filter_tags"); ok {
for _, s := range attr.([]interface{}) {
filterTags = append(filterTags, s.(string))
}
}

hostTags := []string{}

if attr, ok := d.GetOk("host_tags"); ok {
for _, s := range attr.([]interface{}) {
hostTags = append(hostTags, s.(string))
}
}

accountSpecificNamespaceRules := make(map[string]bool)

if attr, ok := d.GetOk("account_specific_namespace_rules"); ok {
// TODO: this is not very defensive, test if we can fail on non bool input
for k, v := range attr.(map[string]interface{}) {
accountSpecificNamespaceRules[k] = v.(bool)
}
}
iaws.FilterTags = filterTags
iaws.HostTags = hostTags
iaws.AccountSpecificNamespaceRules = accountSpecificNamespaceRules
return iaws
}

func resourceDatadogIntegrationAwsCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*datadog.Client)

accountID := d.Get("account_id").(string)
roleName := d.Get("role_name").(string)

iaws := resourceDatadogIntegrationAwsPrepareCreateRequest(d, accountID, roleName)
response, err := client.CreateIntegrationAWS(&iaws)

if err != nil {
return fmt.Errorf("error creating a Amazon Web Services integration: %s", err.Error())
}

d.SetId(fmt.Sprintf("%s:%s", accountID, roleName))
d.Set("external_id", response.ExternalID)

return resourceDatadogIntegrationAwsRead(d, meta)
}

func resourceDatadogIntegrationAwsRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*datadog.Client)

accountID, roleName := accountAndRoleFromID(d.Id())

integrations, err := client.GetIntegrationAWS()
if err != nil {
return err
}
for _, integration := range *integrations {
if integration.GetAccountID() == accountID && integration.GetRoleName() == roleName {
d.Set("account_id", integration.GetAccountID())
d.Set("role_name", integration.GetRoleName())
d.Set("filter_tags", integration.FilterTags)
d.Set("host_tags", integration.HostTags)
d.Set("account_specific_namespace_rules", integration.AccountSpecificNamespaceRules)
return nil
}
}
return fmt.Errorf("error getting a Amazon Web Services integration: account_id=%s, role_name=%s", accountID, roleName)
}

func resourceDatadogIntegrationAwsUpdate(d *schema.ResourceData, meta interface{}) error {
// Unfortunately the PUT operation for updating the AWS configuration is not available at the moment.
// However this feature is one we have in our backlog. I don't know if it's scheduled for delivery short-term,
// however I will follow-up after reviewing with product management.
// ©

// UpdateIntegrationAWS function:
// func (client *Client) UpdateIntegrationAWS(awsAccount *IntegrationAWSAccount) (*IntegrationAWSAccountCreateResponse, error) {
// var out IntegrationAWSAccountCreateResponse
// if err := client.doJsonRequest("PUT", "/v1/integration/aws", awsAccount, &out); err != nil {
// return nil, err
// }
// return &out, nil
// }

client := meta.(*datadog.Client)

accountID, roleName := accountAndRoleFromID(d.Id())

iaws := resourceDatadogIntegrationAwsPrepareCreateRequest(d, accountID, roleName)

_, err := client.CreateIntegrationAWS(&iaws)
if err != nil {
return fmt.Errorf("error updating a Amazon Web Services integration: %s", err.Error())
}

return resourceDatadogIntegrationAwsRead(d, meta)
}

func resourceDatadogIntegrationAwsDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*datadog.Client)
accountID, roleName := accountAndRoleFromID(d.Id())

if err := client.DeleteIntegrationAWS(
&datadog.IntegrationAWSAccountDeleteRequest{
AccountID: datadog.String(accountID),
RoleName: datadog.String(roleName),
},
); err != nil {
return fmt.Errorf("error deleting a Amazon Web Services integration: %s", err.Error())
}

return nil
}

func resourceDatadogIntegrationAwsImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
if err := resourceDatadogIntegrationAwsRead(d, meta); err != nil {
return nil, err
}
d.Set("external_id", os.Getenv("EXTERNAL_ID"))
return []*schema.ResourceData{d}, nil
}
25 changes: 25 additions & 0 deletions datadog/resource_datadog_integration_aws_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package datadog

import (
"testing"
)

func TestAccountAndRoleFromID(t *testing.T) {
cases := map[string]struct {
id string
accountID string
roleName string
}{
"basic": {"1234:qwe", "1234", "qwe"},
"underscores": {"1234:qwe_rty_asd", "1234", "qwe_rty_asd"},
}
for name, tc := range cases {
accountID, roleName := accountAndRoleFromID(tc.id)
if accountID != tc.accountID {
t.Errorf("%s: account ID '%s' didn't match `%s`", name, accountID, tc.accountID)
}
if roleName != tc.roleName {
t.Errorf("%s: role name '%s' didn't match `%s`", name, roleName, tc.roleName)
}
}
}
61 changes: 61 additions & 0 deletions website/docs/r/integration_aws.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
layout: "datadog"
page_title: "Datadog: datadog_integration_aws"
sidebar_current: "docs-datadog-resource-integration_aws"
description: |-
Provides a Datadog - Amazon Web Services integration resource. This can be used to create and manage the integrations.
---

# datadog_integration_aws

Provides a Datadog - Amazon Web Services integration resource. This can be used to create and manage Datadog - Amazon Web Services integration.

Update operations are currently not supported with datadog API so any change forces a new resource.

## Example Usage

```hcl
# Create a new Datadog - Amazon Web Services integration
resource "datadog_integration_aws" "sandbox" {
account_id = "1234567890"
role_name = "DatadogAWSIntegrationRole"
filter_tags = ["key:value"]
host_tags = ["key:value", "key2:value2"]
account_specific_namespace_rules = {
"auto_scaling" = false
"opsworks" = false
}
}
```

## Argument Reference

The following arguments are supported:

* `account_id` - (Required) Your AWS Account ID without dashes.
* `role_name` - (Required) Your Datadog role delegation name.
* `filter_tags` - (Optional) Array of EC2 tags (in the form `key:value`) defines a filter that Datadog use when collecting metrics from EC2. Wildcards, such as `?` (for single characters) and `*` (for multiple characters) can also be used.

Only hosts that match one of the defined tags will be imported into Datadog. The rest will be ignored. Host matching a given tag can also be excluded by adding `!` before the tag.

e.x. `env:production,instance-type:c1.*,!region:us-east-1`

* `host_tags` - (Optinal) Array of tags (in the form key:value) to add to all hosts and metrics reporting through this integration.
* `account_specific_namespace_rules` - (Optinal) Enables or disables metric collection for specific AWS namespaces for this AWS account only. A list of namespaces can be found at the [available namespace rules API endpoint](https://api.datadoghq.com/api/v1/integration/aws/available_namespace_rules).

### See also
* [Datadog API Reference > Integrations > AWS](https://docs.datadoghq.com/api/?lang=bash#aws)

## Attributes Reference

The following attributes are exported:

* `external_id` - AWS External ID

## Import

Amazon Web Services integrations can be imported using their `account ID` and `role name` separated with a colon (`:`), while the `external_id` should be passed by setting an environment variable called `EXTERNAL_ID`

```
$ EXTERNAL_ID=${external_id} terraform import datadog_integration_aws.test ${account_id}:${role_name}
```

0 comments on commit 6f35249

Please sign in to comment.