Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added AWS IAM data helpers #255

Merged
merged 2 commits into from
Aug 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
222 changes: 222 additions & 0 deletions access/data_aws_policies.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package access

import (
"encoding/json"
"fmt"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

type awsIamPolicy struct {
Version string `json:"Version,omitempty"`
ID string `json:"Id,omitempty"`
Statements []*awsIamPolicyStatement `json:"Statement"`
}

type awsIamPolicyStatement struct {
Sid string `json:"Sid,omitempty"`
Effect string `json:"Effect,omitempty"`
Actions interface{} `json:"Action,omitempty"`
NotActions interface{} `json:"NotAction,omitempty"`
Resources interface{} `json:"Resource,omitempty"`
NotResources interface{} `json:"NotResource,omitempty"`
Principal map[string]string `json:"Principal,omitempty"`
Condition map[string]map[string]string `json:"Condition,omitempty"`
}

func DataAwsCrossAccountRolicy() *schema.Resource {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a docs page

return &schema.Resource{
Read: func(d *schema.ResourceData, m interface{}) error {
policy := awsIamPolicy{
Statements: []*awsIamPolicyStatement{
{
Effect: "Allow",
Actions: []string{
"ec2:AssociateDhcpOptions",
"ec2:AssociateIamInstanceProfile",
"ec2:AssociateRouteTable",
"ec2:AttachInternetGateway",
"ec2:AttachVolume",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CancelSpotInstanceRequests",
"ec2:CreateDhcpOptions",
"ec2:CreateInternetGateway",
"ec2:CreateKeyPair",
"ec2:CreateRoute",
"ec2:CreateSecurityGroup",
"ec2:CreateSubnet",
"ec2:CreateTags",
"ec2:CreateVolume",
"ec2:CreateVpc",
"ec2:DeleteInternetGateway",
"ec2:DeleteKeyPair",
"ec2:DeleteRoute",
"ec2:DeleteRouteTable",
"ec2:DeleteSecurityGroup",
"ec2:DeleteSubnet",
"ec2:DeleteTags",
"ec2:DeleteVolume",
"ec2:DeleteVpc",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeNetworkAcls",
"ec2:DescribeInternetGateways",
"ec2:DescribeVpcAttribute",
"ec2:DescribeIamInstanceProfileAssociations",
"ec2:DescribeInstanceStatus",
"ec2:DescribeInstances",
"ec2:DescribePrefixLists",
"ec2:DescribeReservedInstancesOfferings",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSpotInstanceRequests",
"ec2:DescribeSpotPriceHistory",
"ec2:DescribeSubnets",
"ec2:DescribeVolumes",
"ec2:DescribeVpcs",
"ec2:DetachInternetGateway",
"ec2:DisassociateIamInstanceProfile",
"ec2:ModifyVpcAttribute",
"ec2:ReplaceIamInstanceProfileAssociation",
"ec2:RequestSpotInstances",
"ec2:RevokeSecurityGroupEgress",
"ec2:RevokeSecurityGroupIngress",
"ec2:RunInstances",
"ec2:TerminateInstances",
"ec2:CreatePlacementGroup",
"ec2:DeletePlacementGroup",
"ec2:DescribePlacementGroups",
},
Resources: "*",
},
{
Effect: "Allow",
Actions: []string{
"iam:CreateServiceLinkedRole",
"iam:PutRolePolicy",
},
Resources: "arn:aws:iam::*:role/aws-service-role/spot.amazonaws.com/AWSServiceRoleForEC2Spot",
Condition: map[string]map[string]string{
"StringLike": {
"iam:AWSServiceName": "spot.amazonaws.com",
},
},
},
},
}
if passRoleARNs, ok := d.GetOk("pass_role_arns"); ok {
policy.Statements = append(policy.Statements, &awsIamPolicyStatement{
Effect: "Allow",
Actions: "iam:PassRole",
Resources: passRoleARNs,
})
}
policyJSON, err := json.MarshalIndent(policy, "", " ")
if err != nil {
return err
}
return d.Set("json", string(policyJSON))
},
Schema: map[string]*schema.Schema{
"pass_role_arns": {
Type: schema.TypeList,
Elem: schema.TypeString,
Optional: true,
},
"json": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func DataAwsAssumeRolePolicy() *schema.Resource {
return &schema.Resource{
Read: func(d *schema.ResourceData, m interface{}) error {
policyJSON, err := json.MarshalIndent(awsIamPolicy{
Statements: []*awsIamPolicyStatement{
{
Effect: "Allow",
Actions: "sts:AssumeRole",
Condition: map[string]map[string]string{
"StringEquals": {
"sts:ExternalId": d.Get("external_id").(string),
},
},
Principal: map[string]string{
"AWS": fmt.Sprintf("arn:aws:iam::%s:root", d.Get("databricks_account_id").(string)),
},
},
},
}, "", " ")
if err != nil {
return err
}
return d.Set("json", string(policyJSON))
},
Schema: map[string]*schema.Schema{
"databricks_account_id": {
Type: schema.TypeString,
Default: "414351767826",
Optional: true,
},
"external_id": {
Type: schema.TypeString,
Required: true,
},
"json": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func DataAwsBucketPolicy() *schema.Resource {
return &schema.Resource{
Read: func(d *schema.ResourceData, m interface{}) error {
policyJSON, err := json.MarshalIndent(awsIamPolicy{
Statements: []*awsIamPolicyStatement{
{
Effect: "Allow",
Actions: []string{
"s3:GetObject",
"s3:GetObjectVersion",
"s3:PutObject",
"s3:DeleteObject",
"s3:ListBucket",
"s3:GetBucketLocation",
},
Resources: []string{
fmt.Sprintf("arn:aws:s3:::%s/*", d.Get("bucket_name").(string)),
fmt.Sprintf("arn:aws:s3:::%s", d.Get("bucket_name").(string)),
},
Principal: map[string]string{
"AWS": fmt.Sprintf("arn:aws:iam::%s:root", d.Get("databricks_account_id").(string)),
},
},
},
}, "", " ")
if err != nil {
return err
}
return d.Set("json", string(policyJSON))
},
Schema: map[string]*schema.Schema{
"databricks_account_id": {
Type: schema.TypeString,
Default: "414351767826",
Optional: true,
},
"bucket_name": {
Type: schema.TypeString,
Required: true,
},
"json": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
48 changes: 48 additions & 0 deletions docs/data-sources/aws_assume_role_policy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# databricks_aws_assume_role_policy Data Source

This data source constructs necessary AWS STS assume role policy for you.

## Example Usage

End-to-end example of provisioning Cross-account IAM role:

```hcl
variable "account_id" {
type = string
description = "External ID you find on https://accounts.cloud.databricks.com/#aws"
}

data "databricks_aws_crossaccount_policy" "this" {}

resource "aws_iam_policy" "cross_account_policy" {
name = "${var.prefix}-crossaccount-iam-policy"
policy = data.databricks_aws_crossaccount_policy.this.json
}

data "databricks_aws_assume_role_policy" "this" {
external_id = var.account_id
}

resource "aws_iam_role" "cross_account" {
name = "${var.prefix}-crossaccount-iam-role"
assume_role_policy = data.databricks_aws_assume_role_policy.this.json
description = "Grants Databricks full access to VPC resources"
}

resource "aws_iam_role_policy_attachment" "cross_account" {
policy_arn = aws_iam_policy.cross_account_policy.arn
role = aws_iam_role.cross_account.name
}

// required only in case of multiworkspace setup
resource "databricks_mws_credentials" "this" {
provider = databricks.mws
account_id = var.account_id
credentials_name = "${var.prefix}-creds"
role_arn = aws_iam_role.cross_account.arn
}
```

## Attribute Reference
Copy link
Contributor

@stikkireddy stikkireddy Aug 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the other docs in the repo make a clear distinction between attribute and argument.

Argument should be user inputs where attributes are outputs/read only values computed by the resource/datasource.

This should use the naming convention

Argument reference

  • <varname> - (Required/Computed/Optional) (Datatype) (content)

Attribute reference

  • <varname> - (Datatype) (content)


This data source takes `external_id` argument, which is your master account id, which you can find as External ID on https://accounts.cloud.databricks.com/#aws. AWS IAM Policy JSON documents are available in `json` property that is exported from this data source.
27 changes: 27 additions & 0 deletions docs/data-sources/aws_bucket_policy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# databricks_aws_bucket_policy Data Source

This datasource configures simple access policy for AWS S3 buckets, so that Databricks can access data in it.

## Example Usage

```hcl
resource "aws_s3_bucket" "this" {
bucket = "things"
region = "eu-west-1"
acl = "private"
force_destroy = true
}

data "databricks_aws_bucket_policy" "stuff" {
bucket_name = aws_s3_bucket.this.bucket
}

resource "aws_s3_bucket_policy" "this" {
bucket = aws_s3_bucket.this.id
policy = data.databricks_aws_bucket_policy.this.json
}
```
## Argument Reference
Copy link
Contributor

@stikkireddy stikkireddy Aug 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the other docs in the repo make a clear distinction between attribute and argument.

Argument should be user inputs where attributes are outputs/read only values computed by the resource/datasource.

The bucket_name should be an argument where the json is an attribute.

This should use the naming convention

Argument reference

  • <varname> - (Required/Computed/Optional) (Datatype) (content)

Attribute reference

  • <varname> - (Datatype) (content)


* `bucket_name` - (Required) AWS S3 Bucket name for which to generate policy document.
* `json` - (Read-only) AWS IAM Policy JSON document to grand Databricks full access to bucket.
15 changes: 9 additions & 6 deletions provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ import (
func DatabricksProvider() terraform.ResourceProvider {
return &schema.Provider{
DataSourcesMap: map[string]*schema.Resource{
"databricks_zones": compute.DataSourceClusterZones(),
"databricks_default_user_roles": identity.DataSourceDefaultUserRoles(),
"databricks_dbfs_file": storage.DataSourceDBFSFile(),
"databricks_dbfs_file_paths": storage.DataSourceDBFSFilePaths(),
"databricks_notebook": workspace.DataSourceNotebook(),
"databricks_notebook_paths": workspace.DataSourceNotebookPaths(),
"databricks_aws_crossaccount_policy": access.DataAwsCrossAccountRolicy(),
"databricks_aws_assume_role_policy": access.DataAwsAssumeRolePolicy(),
"databricks_aws_bucket_policy": access.DataAwsBucketPolicy(),
"databricks_dbfs_file": storage.DataSourceDBFSFile(),
"databricks_dbfs_file_paths": storage.DataSourceDBFSFilePaths(),
"databricks_default_user_roles": identity.DataSourceDefaultUserRoles(),
"databricks_notebook": workspace.DataSourceNotebook(),
"databricks_notebook_paths": workspace.DataSourceNotebookPaths(),
"databricks_zones": compute.DataSourceClusterZones(),
},
ResourcesMap: map[string]*schema.Resource{
"databricks_secret": access.ResourceSecret(),
Expand Down