diff --git a/provisioner/terraform/resources.go b/provisioner/terraform/resources.go index c6d1e99ab86f6..2e075a5b02a53 100644 --- a/provisioner/terraform/resources.go +++ b/provisioner/terraform/resources.go @@ -120,6 +120,42 @@ func ConvertResources(module *tfjson.StateModule, rawGraph string) ([]*proto.Res } } + type appAttributes struct { + AgentID string `mapstructure:"agent_id"` + Name string `mapstructure:"name"` + Icon string `mapstructure:"icon"` + URL string `mapstructure:"url"` + Command string `mapstructure:"command"` + RelativePath bool `mapstructure:"relative_path"` + } + // Associate Apps with agents. + for _, resource := range tfResources { + if resource.Type != "coder_app" { + continue + } + var attrs appAttributes + err = mapstructure.Decode(resource.AttributeValues, &attrs) + if err != nil { + return nil, xerrors.Errorf("decode app attributes: %w", err) + } + if attrs.Name == "" { + // Default to the resource name if none is set! + attrs.Name = resource.Name + } + for _, agent := range agents { + if agent.Id != attrs.AgentID { + continue + } + agent.Apps = append(agent.Apps, &proto.App{ + Name: attrs.Name, + Command: attrs.Command, + Url: attrs.URL, + Icon: attrs.Icon, + RelativePath: attrs.RelativePath, + }) + } + } + for _, resource := range tfResources { if resource.Mode == tfjson.DataResourceMode { continue @@ -127,10 +163,43 @@ func ConvertResources(module *tfjson.StateModule, rawGraph string) ([]*proto.Res if resource.Type == "coder_agent" || resource.Type == "coder_agent_instance" || resource.Type == "coder_app" { continue } + agents := findAgents(resourceDependencies, agents, convertAddressToLabel(resource.Address)) + for _, agent := range agents { + // Didn't use instance identity. + if agent.GetToken() != "" { + continue + } + + // These resource types are for automatically associating an instance ID + // with an agent for authentication. + key, isValid := map[string]string{ + "google_compute_instance": "instance_id", + "aws_instance": "id", + "azurerm_linux_virtual_machine": "id", + "azurerm_windows_virtual_machine": "id", + }[resource.Type] + if !isValid { + // The resource type doesn't support + // automatically setting the instance ID. + continue + } + instanceIDRaw, valid := resource.AttributeValues[key] + if !valid { + continue + } + instanceID, valid := instanceIDRaw.(string) + if !valid { + continue + } + agent.Auth = &proto.Agent_InstanceId{ + InstanceId: instanceID, + } + } + resources = append(resources, &proto.Resource{ Name: resource.Name, Type: resource.Type, - Agents: findAgents(resourceDependencies, agents, convertAddressToLabel(resource.Address)), + Agents: agents, }) } diff --git a/provisioner/terraform/resources_test.go b/provisioner/terraform/resources_test.go index ded477a83208a..03386557151e6 100644 --- a/provisioner/terraform/resources_test.go +++ b/provisioner/terraform/resources_test.go @@ -11,6 +11,7 @@ import ( tfjson "github.com/hashicorp/terraform-json" "github.com/stretchr/testify/require" + "github.com/coder/coder/cryptorand" "github.com/coder/coder/provisioner/terraform" "github.com/coder/coder/provisionersdk/proto" ) @@ -74,6 +75,21 @@ func TestConvertResources(t *testing.T) { Auth: &proto.Agent_Token{}, }}, }}, + "multiple-apps": {{ + Name: "dev", + Type: "null_resource", + Agents: []*proto.Agent{{ + Name: "dev1", + OperatingSystem: "linux", + Architecture: "amd64", + Apps: []*proto.App{{ + Name: "app1", + }, { + Name: "app2", + }}, + Auth: &proto.Agent_Token{}, + }}, + }}, } { folderName := folderName expected := expected @@ -140,3 +156,69 @@ func TestConvertResources(t *testing.T) { }) } } + +func TestInstanceIDAssociation(t *testing.T) { + t.Parallel() + type tc struct { + Auth string + ResourceType string + InstanceIDKey string + } + for _, tc := range []tc{{ + Auth: "google-instance-identity", + ResourceType: "google_compute_instance", + InstanceIDKey: "instance_id", + }, { + Auth: "aws-instance-identity", + ResourceType: "aws_instance", + InstanceIDKey: "id", + }, { + Auth: "azure-instance-identity", + ResourceType: "azurerm_linux_virtual_machine", + InstanceIDKey: "id", + }, { + Auth: "azure-instance-identity", + ResourceType: "azurerm_windows_virtual_machine", + InstanceIDKey: "id", + }} { + tc := tc + t.Run(tc.ResourceType, func(t *testing.T) { + t.Parallel() + instanceID, err := cryptorand.String(12) + require.NoError(t, err) + resources, err := terraform.ConvertResources(&tfjson.StateModule{ + Resources: []*tfjson.StateResource{{ + Address: "coder_agent.dev", + Type: "coder_agent", + Name: "dev", + AttributeValues: map[string]interface{}{ + "arch": "amd64", + "auth": tc.Auth, + }, + }, { + Address: tc.ResourceType + ".dev", + Type: tc.ResourceType, + Name: "dev", + DependsOn: []string{"coder_agent.dev"}, + AttributeValues: map[string]interface{}{ + tc.InstanceIDKey: instanceID, + }, + }}, + // This is manually created to join the edges. + }, `digraph { + compound = "true" + newrank = "true" + subgraph "root" { + "[root] coder_agent.dev" [label = "coder_agent.dev", shape = "box"] + "[root] `+tc.ResourceType+`.dev" [label = "`+tc.ResourceType+`.dev", shape = "box"] + "[root] `+tc.ResourceType+`.dev" -> "[root] coder_agent.dev" + } +} +`) + require.NoError(t, err) + require.Len(t, resources, 1) + require.Len(t, resources[0].Agents, 1) + require.Equal(t, resources[0].Agents[0].GetInstanceId(), instanceID) + }) + } +} diff --git a/provisioner/terraform/testdata/calling-module/with-module.tf b/provisioner/terraform/testdata/calling-module/calling-module.tf similarity index 100% rename from provisioner/terraform/testdata/calling-module/with-module.tf rename to provisioner/terraform/testdata/calling-module/calling-module.tf diff --git a/provisioner/terraform/testdata/calling-module/calling-module.tfstate.json b/provisioner/terraform/testdata/calling-module/calling-module.tfstate.json index 3f07070815e19..d3c18630106e4 100644 --- a/provisioner/terraform/testdata/calling-module/calling-module.tfstate.json +++ b/provisioner/terraform/testdata/calling-module/calling-module.tfstate.json @@ -16,11 +16,11 @@ "auth": "token", "dir": null, "env": null, - "id": "2f83ed29-c32e-401c-9019-f6cd00ed2b31", + "id": "0f0b7be8-9d82-4b01-b5e5-88279ae4f977", "init_script": "", "os": "linux", "startup_script": null, - "token": "e3cb764c-a792-4d9b-8962-b9218715beef" + "token": "0144998d-4154-495a-902b-0cece2800d76" }, "sensitive_values": {} } @@ -36,7 +36,7 @@ "provider_name": "registry.terraform.io/hashicorp/null", "schema_version": 0, "values": { - "id": "7454863731787788813", + "id": "4090647596312392999", "triggers": null }, "sensitive_values": {}, diff --git a/provisioner/terraform/testdata/chaining-resources/chaining-resources.tfstate.json b/provisioner/terraform/testdata/chaining-resources/chaining-resources.tfstate.json index 17e1cab4852b7..06e6bd12ab28d 100644 --- a/provisioner/terraform/testdata/chaining-resources/chaining-resources.tfstate.json +++ b/provisioner/terraform/testdata/chaining-resources/chaining-resources.tfstate.json @@ -16,11 +16,11 @@ "auth": "token", "dir": null, "env": null, - "id": "13a37216-e26b-4cb3-9c32-b56173f1d7b4", + "id": "299427de-bf0c-45a9-9780-97b5c7ea83e7", "init_script": "", "os": "linux", "startup_script": null, - "token": "863b5c19-069d-4873-90fa-8a99d17e60b5" + "token": "c470ea31-7bb4-49f0-af5a-c1d8ad3f2e1c" }, "sensitive_values": {} }, @@ -32,7 +32,7 @@ "provider_name": "registry.terraform.io/hashicorp/null", "schema_version": 0, "values": { - "id": "9217427594333257339", + "id": "743088824302893232", "triggers": null }, "sensitive_values": {}, @@ -48,7 +48,7 @@ "provider_name": "registry.terraform.io/hashicorp/null", "schema_version": 0, "values": { - "id": "2139093808191769892", + "id": "661153862134717768", "triggers": null }, "sensitive_values": {}, diff --git a/provisioner/terraform/testdata/instance-id/with-instance-id.tf b/provisioner/terraform/testdata/instance-id/instance-id.tf similarity index 100% rename from provisioner/terraform/testdata/instance-id/with-instance-id.tf rename to provisioner/terraform/testdata/instance-id/instance-id.tf diff --git a/provisioner/terraform/testdata/instance-id/instance-id.tfstate.json b/provisioner/terraform/testdata/instance-id/instance-id.tfstate.json index ee302d39daa5b..bd86ce2abf5d5 100644 --- a/provisioner/terraform/testdata/instance-id/instance-id.tfstate.json +++ b/provisioner/terraform/testdata/instance-id/instance-id.tfstate.json @@ -16,11 +16,11 @@ "auth": "google-instance-identity", "dir": null, "env": null, - "id": "9ac1de0f-b30c-44df-9f10-7c3e4a2f86b9", + "id": "a72e01b2-71e9-4498-943f-d1a117c696a0", "init_script": "", "os": "linux", "startup_script": null, - "token": "e1a5d4d0-479b-4d20-9bcf-8309ed6a030f" + "token": "553aedb8-98b2-4f4d-87aa-428ba11afd5c" }, "sensitive_values": {} }, @@ -32,8 +32,8 @@ "provider_name": "registry.terraform.io/coder/coder", "schema_version": 0, "values": { - "agent_id": "9ac1de0f-b30c-44df-9f10-7c3e4a2f86b9", - "id": "1bb18780-24f5-452d-a863-b4f3e15ff7bc", + "agent_id": "a72e01b2-71e9-4498-943f-d1a117c696a0", + "id": "c1552a99-782e-4d2b-a5b6-638b3f7297e4", "instance_id": "example" }, "sensitive_values": {}, @@ -49,7 +49,7 @@ "provider_name": "registry.terraform.io/hashicorp/null", "schema_version": 0, "values": { - "id": "3056446686368085729", + "id": "2578813038449197470", "triggers": null }, "sensitive_values": {}, diff --git a/provisioner/terraform/testdata/multiple-agents/with-multiple-agents.tf b/provisioner/terraform/testdata/multiple-agents/multiple-agents.tf similarity index 100% rename from provisioner/terraform/testdata/multiple-agents/with-multiple-agents.tf rename to provisioner/terraform/testdata/multiple-agents/multiple-agents.tf diff --git a/provisioner/terraform/testdata/multiple-agents/multiple-agents.tfstate.json b/provisioner/terraform/testdata/multiple-agents/multiple-agents.tfstate.json index fd5c9d5585c1c..1ccdbafa45afe 100644 --- a/provisioner/terraform/testdata/multiple-agents/multiple-agents.tfstate.json +++ b/provisioner/terraform/testdata/multiple-agents/multiple-agents.tfstate.json @@ -16,11 +16,11 @@ "auth": "token", "dir": null, "env": null, - "id": "03857334-bbeb-4ad0-9562-faf04890191e", + "id": "04cb673b-c7b5-47c9-a445-f0f7e11f2696", "init_script": "", "os": "linux", "startup_script": null, - "token": "aebe14d8-06b9-49e2-8bd3-156612c5575e" + "token": "95e9607b-1ff3-4a35-8ee8-c61248fb5642" }, "sensitive_values": {} }, @@ -36,11 +36,11 @@ "auth": "token", "dir": null, "env": null, - "id": "e677717c-8f42-422b-b5d5-4f7f2da0af87", + "id": "d97044e4-ce09-4eaf-904e-cd9575f07122", "init_script": "", "os": "darwin", "startup_script": null, - "token": "15c8bb04-48c3-40d5-9fc4-21d172890bf5" + "token": "dc49231a-57e1-4a21-bb74-ae34f753d7bf" }, "sensitive_values": {} }, @@ -56,11 +56,11 @@ "auth": "token", "dir": null, "env": null, - "id": "c6f4a383-e827-4404-8210-97b7c331d2fe", + "id": "8b03bbd1-bd6a-4a54-b083-9d773c91de54", "init_script": "", "os": "windows", "startup_script": null, - "token": "78c8ce5a-677d-429b-b1f6-dc32c03cee5c" + "token": "4b91ef3f-cada-4b27-9872-df2c5a47e24a" }, "sensitive_values": {} }, @@ -72,7 +72,7 @@ "provider_name": "registry.terraform.io/hashicorp/null", "schema_version": 0, "values": { - "id": "4861314035639495631", + "id": "8472507680226374958", "triggers": null }, "sensitive_values": {}, diff --git a/provisioner/terraform/testdata/multiple-apps/multiple-apps.tf b/provisioner/terraform/testdata/multiple-apps/multiple-apps.tf new file mode 100644 index 0000000000000..569ae3321429c --- /dev/null +++ b/provisioner/terraform/testdata/multiple-apps/multiple-apps.tf @@ -0,0 +1,27 @@ +terraform { + required_providers { + coder = { + source = "coder/coder" + version = "0.4.2" + } + } +} + +resource "coder_agent" "dev1" { + os = "linux" + arch = "amd64" +} + +resource "coder_app" "app1" { + agent_id = coder_agent.dev1.id +} + +resource "coder_app" "app2" { + agent_id = coder_agent.dev1.id +} + +resource "null_resource" "dev" { + depends_on = [ + coder_agent.dev1 + ] +} diff --git a/provisioner/terraform/testdata/multiple-apps/multiple-apps.tfplan.dot b/provisioner/terraform/testdata/multiple-apps/multiple-apps.tfplan.dot new file mode 100644 index 0000000000000..30e381a044d33 --- /dev/null +++ b/provisioner/terraform/testdata/multiple-apps/multiple-apps.tfplan.dot @@ -0,0 +1,23 @@ +digraph { + compound = "true" + newrank = "true" + subgraph "root" { + "[root] coder_agent.dev1 (expand)" [label = "coder_agent.dev1", shape = "box"] + "[root] coder_app.app1 (expand)" [label = "coder_app.app1", shape = "box"] + "[root] coder_app.app2 (expand)" [label = "coder_app.app2", shape = "box"] + "[root] null_resource.dev (expand)" [label = "null_resource.dev", shape = "box"] + "[root] provider[\"registry.terraform.io/coder/coder\"]" [label = "provider[\"registry.terraform.io/coder/coder\"]", shape = "diamond"] + "[root] provider[\"registry.terraform.io/hashicorp/null\"]" [label = "provider[\"registry.terraform.io/hashicorp/null\"]", shape = "diamond"] + "[root] coder_agent.dev1 (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]" + "[root] coder_app.app1 (expand)" -> "[root] coder_agent.dev1 (expand)" + "[root] coder_app.app2 (expand)" -> "[root] coder_agent.dev1 (expand)" + "[root] null_resource.dev (expand)" -> "[root] coder_agent.dev1 (expand)" + "[root] null_resource.dev (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/null\"]" + "[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] coder_app.app1 (expand)" + "[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] coder_app.app2 (expand)" + "[root] provider[\"registry.terraform.io/hashicorp/null\"] (close)" -> "[root] null_resource.dev (expand)" + "[root] root" -> "[root] provider[\"registry.terraform.io/coder/coder\"] (close)" + "[root] root" -> "[root] provider[\"registry.terraform.io/hashicorp/null\"] (close)" + } +} + diff --git a/provisioner/terraform/testdata/multiple-apps/multiple-apps.tfplan.json b/provisioner/terraform/testdata/multiple-apps/multiple-apps.tfplan.json new file mode 100644 index 0000000000000..e696305c0c634 --- /dev/null +++ b/provisioner/terraform/testdata/multiple-apps/multiple-apps.tfplan.json @@ -0,0 +1,258 @@ +{ + "format_version": "1.1", + "terraform_version": "1.2.2", + "planned_values": { + "root_module": { + "resources": [ + { + "address": "coder_agent.dev1", + "mode": "managed", + "type": "coder_agent", + "name": "dev1", + "provider_name": "registry.terraform.io/coder/coder", + "schema_version": 0, + "values": { + "arch": "amd64", + "auth": "token", + "dir": null, + "env": null, + "os": "linux", + "startup_script": null + }, + "sensitive_values": {} + }, + { + "address": "coder_app.app1", + "mode": "managed", + "type": "coder_app", + "name": "app1", + "provider_name": "registry.terraform.io/coder/coder", + "schema_version": 0, + "values": { + "command": null, + "icon": null, + "name": null, + "relative_path": null, + "url": null + }, + "sensitive_values": {} + }, + { + "address": "coder_app.app2", + "mode": "managed", + "type": "coder_app", + "name": "app2", + "provider_name": "registry.terraform.io/coder/coder", + "schema_version": 0, + "values": { + "command": null, + "icon": null, + "name": null, + "relative_path": null, + "url": null + }, + "sensitive_values": {} + }, + { + "address": "null_resource.dev", + "mode": "managed", + "type": "null_resource", + "name": "dev", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "triggers": null + }, + "sensitive_values": {} + } + ] + } + }, + "resource_changes": [ + { + "address": "coder_agent.dev1", + "mode": "managed", + "type": "coder_agent", + "name": "dev1", + "provider_name": "registry.terraform.io/coder/coder", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "arch": "amd64", + "auth": "token", + "dir": null, + "env": null, + "os": "linux", + "startup_script": null + }, + "after_unknown": { + "id": true, + "init_script": true, + "token": true + }, + "before_sensitive": false, + "after_sensitive": {} + } + }, + { + "address": "coder_app.app1", + "mode": "managed", + "type": "coder_app", + "name": "app1", + "provider_name": "registry.terraform.io/coder/coder", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "command": null, + "icon": null, + "name": null, + "relative_path": null, + "url": null + }, + "after_unknown": { + "agent_id": true, + "id": true + }, + "before_sensitive": false, + "after_sensitive": {} + } + }, + { + "address": "coder_app.app2", + "mode": "managed", + "type": "coder_app", + "name": "app2", + "provider_name": "registry.terraform.io/coder/coder", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "command": null, + "icon": null, + "name": null, + "relative_path": null, + "url": null + }, + "after_unknown": { + "agent_id": true, + "id": true + }, + "before_sensitive": false, + "after_sensitive": {} + } + }, + { + "address": "null_resource.dev", + "mode": "managed", + "type": "null_resource", + "name": "dev", + "provider_name": "registry.terraform.io/hashicorp/null", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "triggers": null + }, + "after_unknown": { + "id": true + }, + "before_sensitive": false, + "after_sensitive": {} + } + } + ], + "configuration": { + "provider_config": { + "coder": { + "name": "coder", + "full_name": "registry.terraform.io/coder/coder", + "version_constraint": "0.4.2" + }, + "null": { + "name": "null", + "full_name": "registry.terraform.io/hashicorp/null" + } + }, + "root_module": { + "resources": [ + { + "address": "coder_agent.dev1", + "mode": "managed", + "type": "coder_agent", + "name": "dev1", + "provider_config_key": "coder", + "expressions": { + "arch": { + "constant_value": "amd64" + }, + "os": { + "constant_value": "linux" + } + }, + "schema_version": 0 + }, + { + "address": "coder_app.app1", + "mode": "managed", + "type": "coder_app", + "name": "app1", + "provider_config_key": "coder", + "expressions": { + "agent_id": { + "references": [ + "coder_agent.dev1.id", + "coder_agent.dev1" + ] + } + }, + "schema_version": 0 + }, + { + "address": "coder_app.app2", + "mode": "managed", + "type": "coder_app", + "name": "app2", + "provider_config_key": "coder", + "expressions": { + "agent_id": { + "references": [ + "coder_agent.dev1.id", + "coder_agent.dev1" + ] + } + }, + "schema_version": 0 + }, + { + "address": "null_resource.dev", + "mode": "managed", + "type": "null_resource", + "name": "dev", + "provider_config_key": "null", + "schema_version": 0, + "depends_on": [ + "coder_agent.dev1" + ] + } + ] + } + }, + "relevant_attributes": [ + { + "resource": "coder_agent.dev1", + "attribute": [ + "id" + ] + } + ] +} diff --git a/provisioner/terraform/testdata/multiple-apps/multiple-apps.tfstate.dot b/provisioner/terraform/testdata/multiple-apps/multiple-apps.tfstate.dot new file mode 100644 index 0000000000000..30e381a044d33 --- /dev/null +++ b/provisioner/terraform/testdata/multiple-apps/multiple-apps.tfstate.dot @@ -0,0 +1,23 @@ +digraph { + compound = "true" + newrank = "true" + subgraph "root" { + "[root] coder_agent.dev1 (expand)" [label = "coder_agent.dev1", shape = "box"] + "[root] coder_app.app1 (expand)" [label = "coder_app.app1", shape = "box"] + "[root] coder_app.app2 (expand)" [label = "coder_app.app2", shape = "box"] + "[root] null_resource.dev (expand)" [label = "null_resource.dev", shape = "box"] + "[root] provider[\"registry.terraform.io/coder/coder\"]" [label = "provider[\"registry.terraform.io/coder/coder\"]", shape = "diamond"] + "[root] provider[\"registry.terraform.io/hashicorp/null\"]" [label = "provider[\"registry.terraform.io/hashicorp/null\"]", shape = "diamond"] + "[root] coder_agent.dev1 (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]" + "[root] coder_app.app1 (expand)" -> "[root] coder_agent.dev1 (expand)" + "[root] coder_app.app2 (expand)" -> "[root] coder_agent.dev1 (expand)" + "[root] null_resource.dev (expand)" -> "[root] coder_agent.dev1 (expand)" + "[root] null_resource.dev (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/null\"]" + "[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] coder_app.app1 (expand)" + "[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] coder_app.app2 (expand)" + "[root] provider[\"registry.terraform.io/hashicorp/null\"] (close)" -> "[root] null_resource.dev (expand)" + "[root] root" -> "[root] provider[\"registry.terraform.io/coder/coder\"] (close)" + "[root] root" -> "[root] provider[\"registry.terraform.io/hashicorp/null\"] (close)" + } +} + diff --git a/provisioner/terraform/testdata/multiple-apps/multiple-apps.tfstate.json b/provisioner/terraform/testdata/multiple-apps/multiple-apps.tfstate.json new file mode 100644 index 0000000000000..678926d95cd8e --- /dev/null +++ b/provisioner/terraform/testdata/multiple-apps/multiple-apps.tfstate.json @@ -0,0 +1,88 @@ +{ + "format_version": "1.0", + "terraform_version": "1.2.2", + "values": { + "root_module": { + "resources": [ + { + "address": "coder_agent.dev1", + "mode": "managed", + "type": "coder_agent", + "name": "dev1", + "provider_name": "registry.terraform.io/coder/coder", + "schema_version": 0, + "values": { + "arch": "amd64", + "auth": "token", + "dir": null, + "env": null, + "id": "0ab1ceca-8ce9-4f02-97bb-9f4087739ce9", + "init_script": "", + "os": "linux", + "startup_script": null, + "token": "3a5de99e-5b10-459c-a3d0-0fb0fdf1f43b" + }, + "sensitive_values": {} + }, + { + "address": "coder_app.app1", + "mode": "managed", + "type": "coder_app", + "name": "app1", + "provider_name": "registry.terraform.io/coder/coder", + "schema_version": 0, + "values": { + "agent_id": "0ab1ceca-8ce9-4f02-97bb-9f4087739ce9", + "command": null, + "icon": null, + "id": "5b6a8a53-0368-4313-89cd-65dc2d9daa2d", + "name": null, + "relative_path": null, + "url": null + }, + "sensitive_values": {}, + "depends_on": [ + "coder_agent.dev1" + ] + }, + { + "address": "coder_app.app2", + "mode": "managed", + "type": "coder_app", + "name": "app2", + "provider_name": "registry.terraform.io/coder/coder", + "schema_version": 0, + "values": { + "agent_id": "0ab1ceca-8ce9-4f02-97bb-9f4087739ce9", + "command": null, + "icon": null, + "id": "2b421408-9538-422a-8254-df4b629d0a34", + "name": null, + "relative_path": null, + "url": null + }, + "sensitive_values": {}, + "depends_on": [ + "coder_agent.dev1" + ] + }, + { + "address": "null_resource.dev", + "mode": "managed", + "type": "null_resource", + "name": "dev", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "2309999552249167252", + "triggers": null + }, + "sensitive_values": {}, + "depends_on": [ + "coder_agent.dev1" + ] + } + ] + } + } +}