{"id":13930217,"url":"https://github.com/JupiterOne/terraform-provider-jupiterone","last_synced_at":"2025-07-19T12:32:01.151Z","repository":{"id":37864049,"uuid":"255638121","full_name":"JupiterOne/terraform-provider-jupiterone","owner":"JupiterOne","description":"Terraform provider for JupiterOne","archived":false,"fork":false,"pushed_at":"2024-11-14T21:04:33.000Z","size":5964,"stargazers_count":11,"open_issues_count":20,"forks_count":7,"subscribers_count":12,"default_branch":"main","last_synced_at":"2024-11-22T19:02:50.475Z","etag":null,"topics":["jupiterone","terraform","terraform-provider"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JupiterOne.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":".github/SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-04-14T14:50:19.000Z","updated_at":"2024-11-14T20:46:47.000Z","dependencies_parsed_at":"2023-02-19T06:01:16.419Z","dependency_job_id":"30fe54d7-e57f-4dc4-9914-d6af6965beeb","html_url":"https://github.com/JupiterOne/terraform-provider-jupiterone","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JupiterOne%2Fterraform-provider-jupiterone","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JupiterOne%2Fterraform-provider-jupiterone/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JupiterOne%2Fterraform-provider-jupiterone/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JupiterOne%2Fterraform-provider-jupiterone/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JupiterOne","download_url":"https://codeload.github.com/JupiterOne/terraform-provider-jupiterone/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226607618,"owners_count":17658484,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jupiterone","terraform","terraform-provider"],"created_at":"2024-08-07T18:05:15.682Z","updated_at":"2025-07-19T12:32:01.145Z","avatar_url":"https://github.com/JupiterOne.png","language":"Go","funding_links":[],"categories":["terraform"],"sub_categories":[],"readme":"# Terraform Provider JupiterOne\n\n## Requirements\n\n- [Terraform](https://www.terraform.io/downloads.html) 1.0.1\n- [Go](https://golang.org/doc/install) 1.18 (to build the provider plugin)\n\n## Using the provider\n\nAdd the jupiterone provider to your project's terraform:\n\n```hcl\nterraform {\n  required_providers {\n    jupiterone = {\n      source  = \"JupiterOne/jupiterone\"\n      version = \"x.x.x\" # Replace with desired version\n    }\n  }\n}\n\nprovider \"jupiterone\" {\n  # Configuration options\n  account_id = \"xxxxx\"\n  api_key = \"xxxx\"\n  region  = \"us\"\n}\n```\n\n## Example Usage\n\nSee the [examples](./examples) directory\n\n## Building The Provider\n\n1. Install [Go](https://go.dev/doc/install) and `make`\n1. Clone the repository\n1. Enter the repository directory\n1. Build the provider with `make build` and then `go install. `\n1. Install the provider locally as referenced [here](#using-development-environment-provider-locally)\n\n## Adding Dependencies\n\nThis provider uses [Go modules](https://github.com/golang/go/wiki/Modules).\nPlease see the Go documentation for the most up to date information about using Go modules.\n\nTo add a new dependency `github.com/author/dependency` to your Terraform provider:\n\n```shell\ngo get github.com/author/dependency\ngo mod tidy\n```\n\n## Developing the Provider\n\nIf this is your first time developing in go, or developing a terraform provider, it may be wise to do some of the [GO Terraform Plugin Framework tutorial](https://developer.hashicorp.com/terraform/tutorials/providers-plugin-framework/providers-plugin-framework-provider). This is what is used to build this provider.\n\n### Building\n\nIf you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (please check the [requirements](https://github.com/jupiterone/terraform-provider-jupiterone#requirements) before proceeding). To compile the provider, run `make build`.\n\n```shell\nmake build\n\n# If the above command doesn't work, try the next command in the root directory\ngo install .\n```\n\n### Adding a new resource\n\n#### Create resource file\n\nStart your resource development by adding a `jupiterone/resource_[j1_entity].go` file. You should take a look at another file, such as the `resource_user_group.go` to get an idea of what you need in this file, but we will be going into depth on some of the file contents below.\n\n#### J1EntityResource struct\n\nThis type is the base type of the terraform resource you are creating. The functions defined in the rest of the file are added to the interface of this type and enable all further functionality.\n\nYou will almost always have the `version` and `qlient` fields in this type. They are initialized in the base provider and added to an instance of your type in the `Configure` method.\n\n```go\ntype J1EntityResource struct {\n\tversion string\n\tqlient  graphql.Client\n}\n```\n\n#### J1EntityModel struct\n\nThis is the type that represents the terraform resource's state. Generally this is the go equivalent of your graphql resource. You can see that there are json and tfsdk field name definitions. I am not yet sure what the json fields are used for, but the tfsdk fields are used to map the terraform fields to this go type.\n\nThis J1EntityModel is using example fields from the `UserGroupModel`, so you have to modify the fields to represent the entity that you are working with. You will probably have an `Id`, but may not a `Name`, `Description`, etc.\n\n```go\n// J1EntityModel is the terraform HCL representation of a user group.\ntype J1EntityModel struct {\n\tId          types.String \t\t\t\t\t\t\t`json:\"id,omitempty\" tfsdk:\"id\"`\n\tName        types.String \t\t\t\t\t\t\t`json:\"groupName,omitempty\" tfsdk:\"name\"`\n\tDescription types.String \t\t\t\t\t\t\t`json:\"groupDescription,omitempty\" tfsdk:\"description\"`\n\tPermissions []string     \t\t\t\t\t\t\t`json:\"groupAbacPermission,omitempty\" tfsdk:\"permissions\"`\n\tQueryPolicy []map[string][]string\t\t\t`json:\"groupQueryPolicy,omitempty\" tfsdk:\"query_policy\"`\n}\n```\n\n#### NewJ1EntityResource function\n\nThis function is what is used to make the provider aware of your new resource. This will be added to the `Resources` function in the `provider.go` file.\n\n```go\nfunc NewJ1EntityResource() resource.Resource {\n\treturn \u0026J1EntityResource{}\n}\n```\n\n#### Metadata function\n\nThis function is simply used to define your TypeName for the terraform resource. This is the name that will be used when creating resources in your terraform.\n\n```go\nfunc (*J1EntityResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {\n\tresp.TypeName = req.ProviderTypeName + \"_j1_entity\"\n}\n```\n\nIn terraform the name will be used like this:\n\n```terraform\nresource \"jupiterone_j1_entity\" \"j1_entity_1\" {...}\n```\n\n#### Schema function\n\nThis function is used to define the schema and documentation for the terraform people will write to build your resource. The terraform go provider lib will use this schema to parse the consumers terraform, validate it, and map it to the J1EntityResource go type.\n\nWhat you need in this function is totally dependent on what your entity structure looks like. Take a look at some of the other resource.go files to get an idea of what you may need here.\n\n#### Configure function\n\nThis function is used to add the version and qlient to the J1EntityResource. The qlient is then used to make http calls to the J1 graphql api. Your `Configure` method should look much the same as below.\n\n```go\nfunc (r *J1EntityResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {\n\t// Prevent panic if the provider has not been configured.\n\tif req.ProviderData == nil {\n\t\treturn\n\t}\n\n\tp, ok := req.ProviderData.(*JupiterOneProvider)\n\n\tif !ok {\n\t\tresp.Diagnostics.AddError(\n\t\t\t\"Unexpected Resource Configure Type\",\n\t\t\tfmt.Sprintf(\"Expected JupiterOneProvider, got: %T. Please report this issue to the provider developers.\", req.ProviderData),\n\t\t)\n\n\t\treturn\n\t}\n\n\tr.version = p.version\n\tr.qlient = p.Qlient\n}\n```\n\n#### Create function\n\nThis function is used to create the actual resource. It parses out the J1EntityModel from terraform plan and you work with that data how ever you need, which is mainly just calling the graphql api to create your resource.\n\nFollow this general structure for your create function and look at other resource.go files to get an idea of what you may need here.\n\nNote that if the resource you are creating does not return an `id` from the API, you will need to assign a unique id to the `data.Id` field. This is used by terraform to track the resource. The `user_group_membership` resource is one where this is necessary and can be used as an example.\n\n```go\nfunc (r *J1EntityResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {\n\tvar data J1EntityModel\n\n\t// Read Terraform plan data into the model\n\tresp.Diagnostics.Append(req.Plan.Get(ctx, \u0026data)...)\n\tif resp.Diagnostics.HasError() {\n\t\treturn\n\t}\n\n\t// Entity specific work goes here, such as calling a gql endpoint\n  // Check other files for specifics\n  ...\n\n\tif err != nil {\n\t\tresp.Diagnostics.AddError(\"failed to create j1 entity\", err.Error())\n\t\treturn\n\t}\n\n\tdata.Id = types.StringValue(gqlResponse.Id)\n\n\ttflog.Trace(ctx, \"Created j1 entity\",\n\t\tmap[string]interface{}{\"id\": data.Id})\n\n\tresp.Diagnostics.Append(resp.State.Set(ctx, \u0026data)...)\n}\n```\n\n#### Delete function\n\nThis function is used to delete your terraform resource. It will generally just call a gql endpoint to do that work.\n\n```go\n// Delete implements resource.Resource\nfunc (r *J1EntityResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {\n\tvar data J1EntityModel\n\n\t// Read Terraform ste into the model\n\tresp.Diagnostics.Append(req.State.Get(ctx, \u0026data)...)\n\n\tif resp.Diagnostics.HasError() {\n\t\treturn\n\t}\n\n  // This is an example call to gql endpoint to delete an entity. You swap with your implementation\n\tif _, err := client.DeleteJ1Entity(ctx, r.qlient, data.Id.ValueString()); err != nil {\n\t\tresp.Diagnostics.AddError(\"failed to j1 entity\", err.Error())\n\t}\n}\n```\n\n#### Read function\n\nThis function is used to read your entity from the jupiterone api. This helps terraform know if there have been changes made in the jupiterone application that need to be overwritten with update intervention.\n\n```go\nfunc (r *J1EntityResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {\n\tvar data J1EntityModel\n\n\t// Read Terraform state into the model\n\tresp.Diagnostics.Append(req.State.Get(ctx, \u0026data)...)\n\n\tif resp.Diagnostics.HasError() {\n\t\treturn\n\t}\n\n  // Grab your entity from jupiterone gql api and then map it to the J1EntityModel\n  // See other resource.go files for examples\n\tentity, err := client.GetJ1Entity(ctx, r.qlient, data.Id.ValueString())\n\tif err != nil {\n\t\tif strings.Contains(err.Error(), \"not found\") {\n\t\t\t\tresp.State.RemoveResource(ctx)\n\t\t} else {\n\t\t\t\tresp.Diagnostics.AddError(\"failed to get entity\", err.Error())\n\t\t}\n\t\treturn\n\t}\n\n\tdata.Name = types.StringValue(entity.IamGetGroup.GroupName)\n\tdata.Description = types.StringValue(entity.IamGetGroup.GroupDescription)\n\n\t// Save updated data into Terraform state\n\tresp.Diagnostics.Append(resp.State.Set(ctx, \u0026data)...)\n}\n```\n\n#### ImportState function\n\nThis function simply tells terraform which property to target for the import state. You will generally just copy this function over.\n\n```go\nfunc (*J1EntityResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {\n\tresource.ImportStatePassthroughID(ctx, path.Root(\"id\"), req, resp)\n}\n```\n\n#### Update function\n\nThis function is used to update an entity when terraform finds differences between terraform config and the state of the entity in jupiterone. Your contents will be much like your create function, only you should be calling the update action on the gql api.\n\n```go\n// Update implements resource.Resource\nfunc (r *J1EntityResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {\n\tvar data J1EntityModel\n\n\t// Read Terraform plan data into the model\n\tresp.Diagnostics.Append(req.Plan.Get(ctx, \u0026data)...)\n\n\tif resp.Diagnostics.HasError() {\n\t\treturn\n\t}\n\n  // Entity specific work goes here, such as calling a gql endpoint\n  // Check other files for specifics\n  ...\n\n\tif err != nil {\n\t\tresp.Diagnostics.AddError(\"failed to update j1 entity\", err.Error())\n\t\treturn\n\t}\n\n\ttflog.Trace(ctx, \"Updated j1 entity\",\n\t\tmap[string]interface{}{\"id\": data.Id})\n\n\tresp.Diagnostics.Append(resp.State.Set(ctx, \u0026data)...)\n}\n```\n\n### Adding or Updating GraphQL Queries\n\nThe GraphQL client methods are generated using the\n[khan/genqlient](https://github.com/Khan/genqlient) library. The primary\nadvantages are:\n\n- Compile time query checking\n- Generated full types for all API calls\n\n#### Requirements:\n\n- `node` and `yarn` are installed\n\n#### Add queries and mutations\n\nYou should either update an existing `.graphql` file in the `/jupiterone/internal/client` directory, or create a new one.\n\nIf you create a new one, be sure to add the file to the [genqlient.yaml](jupiterone/internal/client/genqlient.yaml) file `operations` section.\n\n#### Set environment variables\n\nYou should always generate the gql client from the production api so that you do not include any in-progress work from dev. Set these environment variables before running the next commands.\n\n```shell\nexport JUPITERONE_ACCOUNT_ID=:your_account_id\nexport JUPITERONE_API_KEY=:your_api_key\nexport JUPITERONE_REGION=us\n```\n\n#### Generating the client\n\nThese commands will generate several files:\n\n- introspection_result.json\n- jupiterone/internal/client/schema.graphql\n- jupiterone/internal/client/generated.go \u003c-- Only generated file that gets committed to the repository\n\n```shell\nscripts/get_current_schema.bash\nmake generate-client\n```\n\n**NOTE**: If you are getting errors like this: `for is only applicable to operations and arguments`, check whether formatting has changed in the gql file in question. If the formatting has changed, you may be running into the issue documented in this thread: https://github.com/Khan/genqlient/issues/149#issuecomment-958150171\n\n**NOTE**: If you are getting the following error `Cannot find module 'graphql'`, run `yarn` in the root directory of the project.\n\n---\n\n### Testing\n\nIn order to test the provider, you can simply run `make testacc`. Pre-recorded\nAPI responses (cassettes) are read in from\n[jupiterone/cassettes/\\*.yaml](jupiterone/cassettes) files and returned. When\ntests are modified, the cassettes need to be re-recorded.\n\n_Note:_ Recording cassettes creates/updates/destroys real resources. Never run this on\na production JupiterOne organization.\n\nIn order to record cassettes you need to have `JUPITERONE_API_KEY` and `JUPITERONE_ACCOUNT_ID`\nfor your testing organization in your environment.\n\nTo re-record _all_ cassettes:\n\n```sh\nexport JUPITERONE_ACCOUNT_ID=your-account-id\nexport JUPITERONE_API_KEY=xxxxxx\nexport JUPITERONE_REGION=us\nmake cassettes\n```\n\nIf you only need to re-record a subset of your tests, delete the related\ncassette file and run the tests as usual. This takes advantage of `go-vcr`s\ndefault [`ModeRecordOnce`](https://pkg.go.dev/gopkg.in/dnaeon/go-vcr.v3@v3.1.2/recorder#Mode)\nfunctionality.\n\n```sh\nexport JUPITERONE_ACCOUNT_ID=your-account-id\nexport JUPITERONE_API_KEY=xxxxxx\nexport JUPITERONE_REGION=us\nrm jupiterone/cassettes/:some-test.yaml\nmake testacc\n```\n\n### Debugging HTTP Traffic\n\nTo log the HTTP request and response contents, set the `TF_LOG` level to `DEBUG`\nor lower:\n\n```shell\nexport TF_LOG=DEBUG\nmake testacc\n```\n\n## Using development environment provider locally\n\nIn order to check changes you made locally to the provider, you can use the binary you just compiled by adding the following\nto your `~/.terraformrc` file. This is valid for Terraform 0.14+. Please see\n[Terraform's documentation](https://www.terraform.io/docs/cli/config/config-file.html#development-overrides-for-provider-developers)\nfor more details.\n\n```hcl\nprovider_installation {\n\n  # Use /home/$USER/go/bin as an overridden package directory\n  # for the jupiterone provider. This disables the version and checksum\n  # verifications for this provider and forces Terraform to look for the\n  # jupiterone provider plugin in the given directory.\n\n  # Replace $USER with your username. On Mac and Linux systems this can be found\n  # through running \"echo $USER\" in your terminal.\n\tdev_overrides {\n    \"JupiterOne/jupiterone\" = \"/Users/$USER/go/bin\"\n  }\n\n  # For all other providers, install them directly from their origin provider\n  # registries as normal. If you omit this, Terraform will _only_ use\n  # the dev_overrides block, and so no other providers will be available.\n  direct {}\n}\n```\n\nFor information about writing acceptance tests, see the main Terraform [contributing guide](https://github.com/hashicorp/terraform/blob/master/.github/CONTRIBUTING.md#writing-acceptance-tests).\n\n### Releasing the Provider\n\nThis repository contains a GitHub Action configured to automatically build and\npublish assets for release when a tag is pushed that matches the pattern `v*`\n(ie. `v0.1.0`).\n\nA [Goreleaser](https://goreleaser.com/) configuration is provided that produces\nbuild artifacts matching the [layout required](https://www.terraform.io/docs/registry/providers/publishing.html#manually-preparing-a-release)\nto publish the provider in the Terraform Registry.\n\nReleases will appear as drafts. Once marked as published on the GitHub Releases page,\nthey will become available via the Terraform Registry.\n\n### Documentation\n\nTo generate new provider documentation run `make docs`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FJupiterOne%2Fterraform-provider-jupiterone","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FJupiterOne%2Fterraform-provider-jupiterone","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FJupiterOne%2Fterraform-provider-jupiterone/lists"}