├── .go-version ├── jamf ├── const.go ├── resource_jamf_package.go ├── resource_jamf_building_test.go ├── resource_jamf_category_test.go ├── resource_jamf_department_test.go ├── data_source_jamf_building_test.go ├── data_source_jamf_category_test.go ├── data_source_jamf_department_test.go ├── data_source_jamf_department.go ├── data_source_jamf_category.go ├── provider_test.go ├── data_source_jamf_building.go ├── data_source_jamf_staticComputerGroup.go ├── provider.go ├── data_source_jamf_packages.go ├── data_source_jamf_script.go ├── resource_jamf_department.go ├── resource_jamf_category.go ├── data_source_jamf_smartComputerGroup.go ├── resource_jamf_building.go ├── resource_jamf_staticComputerGroup.go ├── resource_jamf_script.go ├── resource_jamf_smartComputerGroup.go ├── data_source_jamf_policy.go └── resource_jamf_policy.go ├── .gitignore ├── scripts └── gofmtcheck.sh ├── website └── docs │ ├── r │ ├── department.html.md │ ├── category.html.md │ ├── staticComputerGroup.html.md │ ├── building.html.md │ ├── smartComputerGroup.html.md │ ├── script.html.md │ └── policy.html.md │ ├── d │ ├── department.html.md │ ├── category.html.md │ ├── building.html.md │ ├── staticComputerGroup.html.md │ ├── package.html.md │ ├── smartComputerGroup.html.md │ ├── script.html.md │ └── policy.html.md │ └── index.html.md ├── main.go ├── README.md ├── LICENCE ├── docs └── DEVELOPMENT.md ├── go.mod ├── .goreleaser.yml └── Makefile /.go-version: -------------------------------------------------------------------------------- 1 | 1.15.5 2 | -------------------------------------------------------------------------------- /jamf/const.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | const ( 4 | AppName = "terraform-provider-jamf" 5 | ) 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.swp 3 | .DS_Store 4 | terraform-provider-jamf 5 | !.gitkeep 6 | dist/ 7 | bin/ 8 | tmp/ -------------------------------------------------------------------------------- /jamf/resource_jamf_package.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | // Jamf does not have official documentation on how to upload an actual package file 4 | // via the API. Therefore I'm not including this by default. If anyone wants to submit 5 | // a PR to enable this "aftermarket" functionality based on JSSImporter or other third 6 | // party packages, then please do. 7 | 8 | // Plz help Jamf, you're our only hope 9 | 10 | // -Yohan 11 | -------------------------------------------------------------------------------- /scripts/gofmtcheck.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Check gofmt 4 | echo "==> Checking that code complies with gofmt requirements..." 5 | gofmt_files=$(gofmt -d -l `find . -name '*.go' | grep -v vendor`) 6 | if [[ -n ${gofmt_files} ]]; then 7 | echo 'gofmt needs running on the following files:' 8 | echo "${gofmt_files}" 9 | echo "You can use the command: \`make fmt\` to reformat code." 10 | exit 1 11 | fi 12 | 13 | exit 0 14 | -------------------------------------------------------------------------------- /website/docs/r/department.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "jamf" 3 | subcategory: "Resources" 4 | page_title: "Jamf: jamf_department" 5 | description: |- 6 | Provides an department. 7 | --- 8 | 9 | # Resource: jamf_department 10 | 11 | Provides an Department. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | resource "jamf_department" "example" { 17 | name = "example" 18 | } 19 | ``` 20 | 21 | ## Argument Reference 22 | 23 | The following arguments are supported: 24 | 25 | * `name` - (Required) The name of the department. 26 | 27 | ## Attributes Reference 28 | 29 | In addition to the above arguments, the following attributes are exported: 30 | 31 | * `id` - The ID of the department. 32 | -------------------------------------------------------------------------------- /website/docs/r/category.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "jamf" 3 | subcategory: "Resources" 4 | page_title: "Jamf: jamf_category" 5 | description: |- 6 | Provides an category. 7 | --- 8 | 9 | # Resource: jamf_category 10 | 11 | Provides an Category. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | resource "jamf_category" "example" { 17 | name = "example" 18 | priority = 9 19 | } 20 | ``` 21 | 22 | ## Argument Reference 23 | 24 | The following arguments are supported: 25 | 26 | * `name` - (Required) The name of the category. 27 | * `priority` - (Required) The name of the priority. 28 | 29 | ## Attributes Reference 30 | 31 | In addition to the above arguments, the following attributes are exported: 32 | 33 | * `id` - The ID of the category. 34 | -------------------------------------------------------------------------------- /jamf/resource_jamf_building_test.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 8 | ) 9 | 10 | func TestAccJamfBuilding_basic(t *testing.T) { 11 | resource.ParallelTest(t, resource.TestCase{ 12 | PreCheck: func() { testAccPreCheck(t) }, 13 | ProviderFactories: providerFactories, 14 | Steps: []resource.TestStep{ 15 | { 16 | Config: testAccCheckJamfBuildingConfigMissingFields, 17 | ExpectError: regexp.MustCompile("The argument \"name\" is required, but no definition was found."), 18 | }, 19 | }, 20 | }) 21 | } 22 | 23 | const ( 24 | testAccCheckJamfBuildingConfigMissingFields = ` 25 | resource "jamf_building" "test" { 26 | }` 27 | ) 28 | -------------------------------------------------------------------------------- /jamf/resource_jamf_category_test.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 8 | ) 9 | 10 | func TestAccJamfCategory_basic(t *testing.T) { 11 | resource.ParallelTest(t, resource.TestCase{ 12 | PreCheck: func() { testAccPreCheck(t) }, 13 | ProviderFactories: providerFactories, 14 | Steps: []resource.TestStep{ 15 | { 16 | Config: testAccCheckJamfCategoryConfigMissingFields, 17 | ExpectError: regexp.MustCompile("The argument \"name\" is required, but no definition was found."), 18 | }, 19 | }, 20 | }) 21 | } 22 | 23 | const ( 24 | testAccCheckJamfCategoryConfigMissingFields = ` 25 | resource "jamf_category" "test" { 26 | }` 27 | ) 28 | -------------------------------------------------------------------------------- /jamf/resource_jamf_department_test.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 8 | ) 9 | 10 | func TestAccJamfDepartment_basic(t *testing.T) { 11 | resource.ParallelTest(t, resource.TestCase{ 12 | PreCheck: func() { testAccPreCheck(t) }, 13 | ProviderFactories: providerFactories, 14 | Steps: []resource.TestStep{ 15 | { 16 | Config: testAccCheckJamfDepartmentConfigMissingFields, 17 | ExpectError: regexp.MustCompile("The argument \"name\" is required, but no definition was found."), 18 | }, 19 | }, 20 | }) 21 | } 22 | 23 | const ( 24 | testAccCheckJamfDepartmentConfigMissingFields = ` 25 | resource "jamf_department" "test" { 26 | }` 27 | ) 28 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" 7 | "github.com/sioncojp/terraform-provider-jamf/jamf" 8 | "log" 9 | ) 10 | 11 | func main() { 12 | var debugMode bool 13 | 14 | flag.BoolVar(&debugMode, "debuggable", false, "set to true to run the provider with support for debuggers like delve") 15 | flag.Parse() 16 | 17 | if debugMode { 18 | err := plugin.Debug(context.Background(), "registry.terraform.io/sioncojp/jamf", 19 | &plugin.ServeOpts{ 20 | ProviderFunc: jamf.Provider, 21 | }) 22 | if err != nil { 23 | log.Println(err.Error()) 24 | } 25 | } else { 26 | plugin.Serve(&plugin.ServeOpts{ 27 | ProviderFunc: jamf.Provider, 28 | }) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /jamf/data_source_jamf_building_test.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 8 | ) 9 | 10 | func TestAccJamfBuildingDataSource_basic(t *testing.T) { 11 | resource.ParallelTest(t, resource.TestCase{ 12 | PreCheck: func() { testAccPreCheck(t) }, 13 | ProviderFactories: providerFactories, 14 | Steps: []resource.TestStep{ 15 | { 16 | Config: testAccDatasourceJamfBuildingConfigMissingFields, 17 | ExpectError: regexp.MustCompile("The argument \"name\" is required, but no definition was found."), 18 | }, 19 | }, 20 | }) 21 | } 22 | 23 | const ( 24 | testAccDatasourceJamfBuildingConfigMissingFields = ` 25 | data "jamf_building" "test" { 26 | }` 27 | ) 28 | -------------------------------------------------------------------------------- /jamf/data_source_jamf_category_test.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 8 | ) 9 | 10 | func TestAccJamfCategoryDataSource_basic(t *testing.T) { 11 | resource.ParallelTest(t, resource.TestCase{ 12 | PreCheck: func() { testAccPreCheck(t) }, 13 | ProviderFactories: providerFactories, 14 | Steps: []resource.TestStep{ 15 | { 16 | Config: testAccDatasourceJamfCategoryConfigMissingFields, 17 | ExpectError: regexp.MustCompile("The argument \"name\" is required, but no definition was found."), 18 | }, 19 | }, 20 | }) 21 | } 22 | 23 | const ( 24 | testAccDatasourceJamfCategoryConfigMissingFields = ` 25 | data "jamf_category" "test" { 26 | }` 27 | ) 28 | -------------------------------------------------------------------------------- /jamf/data_source_jamf_department_test.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 8 | ) 9 | 10 | func TestAccJamfDepartmentDataSource_basic(t *testing.T) { 11 | resource.ParallelTest(t, resource.TestCase{ 12 | PreCheck: func() { testAccPreCheck(t) }, 13 | ProviderFactories: providerFactories, 14 | Steps: []resource.TestStep{ 15 | { 16 | Config: testAccDatasourceJamfDepartmentConfigMissingFields, 17 | ExpectError: regexp.MustCompile("The argument \"name\" is required, but no definition was found."), 18 | }, 19 | }, 20 | }) 21 | } 22 | 23 | const ( 24 | testAccDatasourceJamfDepartmentConfigMissingFields = ` 25 | data "jamf_department" "test" { 26 | }` 27 | ) 28 | -------------------------------------------------------------------------------- /website/docs/d/department.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "jamf" 3 | subcategory: "Data Sources" 4 | page_title: "Jamf: jamf_department" 5 | description: |- 6 | Provides details about a department. 7 | --- 8 | 9 | # Data Source: jamf_department 10 | 11 | Use this data source to get the department information. 12 | 13 | The Department data source allows access to details of a specific 14 | department within Jamf. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | data "jamf_department" "example" { 20 | name = "example" 21 | } 22 | ``` 23 | 24 | ## Argument Reference 25 | 26 | The following arguments are supported: 27 | 28 | * `name` - (Required) The name of the department. 29 | 30 | ## Attributes Reference 31 | 32 | In addition to the above arguments, the following attributes are exported: 33 | 34 | * `id` - The ID of the department. 35 | -------------------------------------------------------------------------------- /website/docs/d/category.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "jamf" 3 | subcategory: "Data Sources" 4 | page_title: "Jamf: jamf_category" 5 | description: |- 6 | Provides details about a category. 7 | --- 8 | 9 | # Data Source: jamf_category 10 | 11 | Use this data source to get the category information. 12 | 13 | The Category data source allows access to details of a specific 14 | category within Jamf. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | data "jamf_category" "example" { 20 | name = "example" 21 | } 22 | ``` 23 | 24 | ## Argument Reference 25 | 26 | The following arguments are supported: 27 | 28 | * `name` - (Required) The name of the category. 29 | 30 | ## Attributes Reference 31 | 32 | In addition to the above arguments, the following attributes are exported: 33 | 34 | * `id` - The ID of the category. 35 | * `priority` - The Priority of the category. -------------------------------------------------------------------------------- /jamf/data_source_jamf_department.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 8 | "github.com/sioncojp/go-jamf-api" 9 | ) 10 | 11 | func dataSourceJamfDepartment() *schema.Resource { 12 | return &schema.Resource{ 13 | ReadContext: dataSourceJamfDepartmentRead, 14 | Schema: map[string]*schema.Schema{ 15 | "name": { 16 | Type: schema.TypeString, 17 | Required: true, 18 | }, 19 | }, 20 | } 21 | } 22 | 23 | func dataSourceJamfDepartmentRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 24 | var diags diag.Diagnostics 25 | c := m.(*jamf.Client) 26 | 27 | resp, err := c.GetDepartmentByName(d.Get("name").(string)) 28 | if err != nil { 29 | return diag.FromErr(err) 30 | } 31 | 32 | d.SetId(resp.GetId()) 33 | d.Set("name", resp.GetName()) 34 | 35 | return diags 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Terraform logo 3 | 4 | 5 | # Terraform Provider for Jamf 6 | 7 | 8 | ## Quick Start 9 | 10 | - [Using the provider](https://registry.terraform.io/providers/sioncojp/jamf/latest/docs) 11 | - [Provider development](docs/DEVELOPMENT.md) 12 | 13 | ```hcl 14 | provider "jamf" { 15 | username = "xxxx" 16 | password = "xxxx" 17 | 18 | # "This is the full url of jamf, xxxx.jamfcloud.com" 19 | url = "xxxx" 20 | } 21 | 22 | data "jamf_department" "example" { 23 | name = "hoge" 24 | } 25 | ``` 26 | 27 | ## Documentation 28 | 29 | Full, comprehensive documentation is available on the Terraform website: 30 | 31 | https://registry.terraform.io/providers/sioncojp/jamf/latest/docs 32 | 33 | ## Release 34 | 35 | ```shell 36 | $ gGPG_FINGERPRINT=xxx GITHUB_TOKEN="xxx" goreleaser release --rm-dist 37 | ``` 38 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | # License 2 | The MIT License 3 | 4 | Copyright Shohei Koyama / sioncojp 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /jamf/data_source_jamf_category.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 8 | "github.com/sioncojp/go-jamf-api" 9 | ) 10 | 11 | func dataSourceJamfCategory() *schema.Resource { 12 | return &schema.Resource{ 13 | ReadContext: dataSourceJamfCategoryRead, 14 | Schema: map[string]*schema.Schema{ 15 | // Computed values. 16 | "name": { 17 | Type: schema.TypeString, 18 | Required: true, 19 | }, 20 | // Computed values. 21 | "priority": { 22 | Type: schema.TypeInt, 23 | Computed: true, 24 | }, 25 | }, 26 | } 27 | } 28 | 29 | func dataSourceJamfCategoryRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 30 | var diags diag.Diagnostics 31 | c := m.(*jamf.Client) 32 | 33 | resp, err := c.GetCategoryByName(d.Get("name").(string)) 34 | if err != nil { 35 | return diag.FromErr(err) 36 | } 37 | 38 | d.SetId(resp.GetId()) 39 | d.Set("name", resp.GetName()) 40 | d.Set("priority", resp.GetPriority()) 41 | 42 | return diags 43 | } 44 | -------------------------------------------------------------------------------- /website/docs/r/staticComputerGroup.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "jamf" 3 | subcategory: "Resources" 4 | page_title: "Jamf: jamf_staticComputerGroup" 5 | description: |- 6 | Provides a static computer group. 7 | --- 8 | 9 | # Resource: jamf_staticComputerGroup 10 | 11 | Provides a static computer group. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | resource "jamf_staticComputerGroup" "test_static_1" { 17 | computer { 18 | id = 1 19 | } 20 | computer { 21 | serial_number = "C06X4489JFD3" 22 | } 23 | name = "Test Static 1" 24 | } 25 | ``` 26 | 27 | ## Argument Reference 28 | 29 | The following arguments are supported: 30 | 31 | * `name` - (Required) The name of the group. 32 | * `computer` - (Optional) List of computers to to be in the group. 33 | * `id` - (Optional) Jamf ID of the computer. 34 | * `serial_number` - (Optional) Serial Number of the computer. 35 | * `site` - (Optional) Site the computer group should be a member of. 36 | * `name` - (Required) Name of the site. 37 | 38 | ## Attributes Reference 39 | 40 | In addition to the above arguments, the following attributes are exported: 41 | 42 | * `id` - The ID of the static computer group. 43 | -------------------------------------------------------------------------------- /website/docs/d/building.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "jamf" 3 | subcategory: "Data Sources" 4 | page_title: "Jamf: jamf_building" 5 | description: |- 6 | Provides details about a building. 7 | --- 8 | 9 | # Data Source: jamf_building 10 | 11 | Use this data source to get the building information. 12 | 13 | The building data source allows access to details of a specific 14 | building within Jamf. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | data "jamf_building" "example" { 20 | name = "example" 21 | } 22 | ``` 23 | 24 | ## Argument Reference 25 | 26 | The following arguments are supported: 27 | 28 | * `name` - (Required) The name of the building. 29 | 30 | ## Attributes Reference 31 | 32 | In addition to the above arguments, the following attributes are exported: 33 | 34 | * `id` - The ID of the building. 35 | * `street_address1` - (Optional) The street address1 of the building. 36 | * `street_address2` - (Optional) The street address2 of the building. 37 | * `city` - (Optional) The vity of the building. 38 | * `state_province` - (Optional) The state province of the building. 39 | * `zip_postal_code` - (Optional) The zip postal code of the building. 40 | * `country` - (Optional) The country of the building. 41 | -------------------------------------------------------------------------------- /website/docs/d/staticComputerGroup.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "jamf" 3 | subcategory: "Data Sources" 4 | page_title: "Jamf: jamf_staticComputerGroup" 5 | description: |- 6 | Provides details about a static computer group. 7 | --- 8 | 9 | # Data Source: jamf_staticComputerGroup 10 | 11 | Use this data source to get the static computer group information. 12 | 13 | The static computer group data source allows access to details of a specific 14 | static computer group within Jamf. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | data "jamf_staticComputerGroup" "test_static_1" { 20 | name = "Test Static 1" 21 | } 22 | ``` 23 | 24 | ## Argument Reference 25 | 26 | The following arguments are supported: 27 | 28 | * `name` - (Required) The name of the group. 29 | * `computer` - (Optional) List of computers to to be in the group. 30 | * `id` - (Optional) Jamf ID of the computer. 31 | * `serial_number` - (Optional) Serial Number of the computer. 32 | * `site` - (Optional) Site the computer group should be a member of. 33 | * `name` - (Optional) Name of the site. 34 | 35 | ## Attributes Reference 36 | 37 | In addition to the above arguments, the following attributes are exported: 38 | 39 | * `id` - The ID of the static computer group. 40 | -------------------------------------------------------------------------------- /website/docs/r/building.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "jamf" 3 | subcategory: "Resources" 4 | page_title: "Jamf: jamf_building" 5 | description: |- 6 | Provides an building. 7 | --- 8 | 9 | # Resource: jamf_building 10 | 11 | Provides an Building. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | resource "jamf_building" "example" { 17 | name = "example" 18 | 19 | street_address1 = "1-1-1" 20 | street_address2 = "example Building" 21 | city = "Shibuya-ku" 22 | state_province = "Tokyo" 23 | zip_postal_code = "111-1111" 24 | country = "Japan" 25 | } 26 | ``` 27 | 28 | ## Argument Reference 29 | 30 | The following arguments are supported: 31 | 32 | * `name` - (Required) The name of the building. 33 | * `street_address1` - (Optional) The street address1 of the building. 34 | * `street_address2` - (Optional) The street address2 of the building. 35 | * `city` - (Optional) The vity of the building. 36 | * `state_province` - (Optional) The state province of the building. 37 | * `zip_postal_code` - (Optional) The zip postal code of the building. 38 | * `country` - (Optional) The country of the building. 39 | 40 | ## Attributes Reference 41 | 42 | In addition to the above arguments, the following attributes are exported: 43 | 44 | * `id` - The ID of the building. 45 | -------------------------------------------------------------------------------- /jamf/provider_test.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 8 | ) 9 | 10 | var providerFactories = map[string]func() (*schema.Provider, error){ 11 | "jamf": func() (*schema.Provider, error) { 12 | return Provider(), nil 13 | }, 14 | } 15 | 16 | // testAccPreCheck ...if non-nil, will be called before any test steps are executed. 17 | // It is commonly used to verify that required values exist for testing, 18 | // such as environment variables containing test keys that are used to 19 | // configure the Provider or Resource under test. 20 | func testAccPreCheck(t *testing.T) { 21 | if !isUsernameSet() { 22 | t.Fatal("JAMF_USERNAME environment variable must be set for acceptance tests") 23 | } 24 | if !isPasswordSet() { 25 | t.Fatal("JAMF_PASSWORD environment variable must be set for acceptance tests") 26 | } 27 | if !isURLSet() { 28 | t.Fatal("JAMF_URL environment variable must be set for acceptance tests") 29 | } 30 | } 31 | 32 | func isUsernameSet() bool { 33 | if os.Getenv("JAMF_USERNAME") != "" { 34 | return true 35 | } 36 | return false 37 | } 38 | 39 | func isPasswordSet() bool { 40 | if os.Getenv("JAMF_PASSWORD") != "" { 41 | return true 42 | } 43 | return false 44 | } 45 | 46 | func isURLSet() bool { 47 | if os.Getenv("JAMF_URL") != "" { 48 | return true 49 | } 50 | return false 51 | } 52 | -------------------------------------------------------------------------------- /docs/DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | ## Development 2 | 3 | ```shell 4 | ### local install terraform & build provider to bin/ 5 | $ make terraform/install 6 | $ make build 7 | 8 | # create .tf file 9 | $ vim jamf.tf 10 | terraform { 11 | required_providers { 12 | jamf = { 13 | } 14 | } 15 | } 16 | 17 | provider "jamf" { 18 | username = "xxxx" 19 | password = "xxxx" 20 | 21 | # "This is the xxxx part of xxxx.jamfcloud.com" 22 | organization = "xxxx" 23 | } 24 | 25 | data "jamf_department" "example" { 26 | name = "hoge" 27 | } 28 | 29 | # Please prepare the following directory structure and files 30 | $ tree 31 | . 32 | ├── jamf.tf 33 | ├── plugins 34 | │   └── registry.terraform.io 35 | │   └── hashicorp 36 | │   └── jamf 37 | │   └── 1.0.0 38 | │   └── darwin_amd64 39 | │   └── terraform-provider-jamf_v1.0.0 40 | └── terraform 41 | 42 | 43 | # After moving to the top directory 44 | # exec (-plugin-dir is full path) 45 | $ ./terraform init -plugin-dir=/....../plugins/ 46 | $ ./terraform plan 47 | ``` 48 | 49 | ## Testing 50 | 51 | There are test and accTest. 52 | 53 | test is the usual test The accTest actually makes the resource and checks it. 54 | 55 | So if you want to do accTest, you need to have the jamf environment to run it. 56 | 57 | ```shell 58 | # test 59 | $ make test 60 | 61 | # acctest 62 | $ JAMF_USERNAME=xxx JAMF_PASSWORD=xxx JAMF_URL=xxx make testacc 63 | 64 | # Running a specific test in acctest 65 | $ JAMF_USERNAME=xxx JAMF_PASSWORD=xxx JAMF_URL=xxx make testacc TESTARGS="-run TestAccJamfDepartments_basic" 66 | ``` 67 | -------------------------------------------------------------------------------- /website/docs/d/package.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "jamf" 3 | subcategory: "Data Sources" 4 | page_title: "Jamf: jamf_package" 5 | description: |- 6 | Provides details about a package. 7 | --- 8 | 9 | # Data Source: jamf_package 10 | 11 | Use this data source to get the package information. 12 | 13 | The package data source allows access to details of a specific 14 | package within Jamf. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | data "jamf_package" "google_chrome" { 20 | name = "GoogleChrome-v87.0.4280.88.pkg" 21 | } 22 | ``` 23 | 24 | ## Argument Reference 25 | 26 | The following arguments are supported: 27 | 28 | * `name` - (Required) The name of the package. 29 | 30 | ## Attributes Reference 31 | 32 | In addition to the above arguments, the following attributes are exported: 33 | 34 | * `id` - The ID of the package. 35 | * `category_name` - The name of the category of which the package pertains. 36 | * `filename` - The name of the package file, Default: `ThisPackageDoesNotExist.pkg`. 37 | * `notes` - The notes of the package. 38 | * `priority` - The priority of the package, Default: `10` 39 | * `fill_existing_users` - If the DMG should fill existing users. 40 | * `boot_volume_required` - If the package should be installed on the boot volume after imaging. 41 | * `allow_uninstalled` - If the package should be allowed to be uninstalled using Jamf remote. 42 | * `os_requirements` - The Os requirements of the package. 43 | * `required_processor` - The required processor of the package, Default: `None`. 44 | * `hash_type` - The hash type of the package. 45 | * `hash_value` - The hash value of the package. 46 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/sioncojp/terraform-provider-jamf 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/agext/levenshtein v1.2.3 // indirect 7 | github.com/fatih/color v1.10.0 // indirect 8 | github.com/golang/protobuf v1.4.3 // indirect 9 | github.com/google/go-cmp v0.5.4 // indirect 10 | github.com/hashicorp/errwrap v1.1.0 // indirect 11 | github.com/hashicorp/go-cleanhttp v0.5.1 12 | github.com/hashicorp/go-hclog v0.15.0 // indirect 13 | github.com/hashicorp/go-multierror v1.1.0 // indirect 14 | github.com/hashicorp/go-plugin v1.4.0 // indirect 15 | github.com/hashicorp/go-uuid v1.0.2 // indirect 16 | github.com/hashicorp/hcl/v2 v2.7.2 // indirect 17 | github.com/hashicorp/terraform-plugin-go v0.2.0 // indirect 18 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.3.0 19 | github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce // indirect 20 | github.com/mitchellh/go-homedir v1.1.0 21 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect 22 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect 23 | github.com/mitchellh/mapstructure v1.4.0 // indirect 24 | github.com/oklog/run v1.1.0 // indirect 25 | github.com/sioncojp/go-jamf-api v0.0.0-20201203145354-708c7247dc7a 26 | github.com/zclconf/go-cty v1.7.0 // indirect 27 | go4.org v0.0.0-20200411211856-f5505b9728dd // indirect 28 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb // indirect 29 | golang.org/x/sys v0.0.0-20201202213521-69691e467435 // indirect 30 | golang.org/x/text v0.3.4 // indirect 31 | google.golang.org/appengine v1.6.7 // indirect 32 | google.golang.org/genproto v0.0.0-20201203001206-6486ece9c497 // indirect 33 | google.golang.org/grpc v1.34.0 // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /website/docs/d/smartComputerGroup.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "jamf" 3 | subcategory: "Data Sources" 4 | page_title: "Jamf: jamf_smartComputerGroup" 5 | description: |- 6 | Provides details about a smart computer group. 7 | --- 8 | 9 | # Data Source: jamf_smartComputerGroup 10 | 11 | Use this data source to get the smart computer group information. 12 | 13 | The smart computer group data source allows access to details of a specific 14 | smart computer group within Jamf. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | data "jamf_smartComputerGroup" "test_smart_1" { 20 | name = "Test Smart 1" 21 | } 22 | ``` 23 | 24 | ## Argument Reference 25 | 26 | The following arguments are supported: 27 | 28 | * `name` - (Required) The name of the group. 29 | * `criteria` - (Optional) List of computers to to be in the group. 30 | * `priority` - (Optional) Priority of the criteria to be evaluated, must start at zero and increase by one. 31 | * `and_or` - (Optional) Join your search with `and` or `or`. Default: `and` 32 | * `name` - (Optional) Name of the criteria to compare with. 33 | * `search_type` - (Optional) The search comparator to use. 34 | * `search_value` - (Optional) The search value to compare against. 35 | * `opening_paren` - (Optional) If the opening parenthese is enabled. Default `false` 36 | * `closing_paren` - (Optional) If the closing parenthese is enabled. Default `false` 37 | * `site` - (Optional) Site the computer group should be a member of. 38 | * `name` - (Optional) Name of the site. 39 | 40 | ## Attributes Reference 41 | 42 | In addition to the above arguments, the following attributes are exported: 43 | 44 | * `id` - The ID of the smart computer group. 45 | -------------------------------------------------------------------------------- /website/docs/d/script.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "jamf" 3 | subcategory: "Data Sources" 4 | page_title: "Jamf: jamf_script" 5 | description: |- 6 | Provides details about a script. 7 | --- 8 | 9 | # Data Sources: jamf_script 10 | 11 | Use this data source to get the script information. 12 | 13 | The script data source allows access to details of a specific script within Jamf. 14 | 15 | ## Example Usage 16 | 17 | ```hcl 18 | data "jamf_script" "test_script_1" { 19 | name = "Test Script 1" 20 | } 21 | ``` 22 | 23 | ## Argument Reference 24 | 25 | The following arguments are supported: 26 | 27 | * `name` - (Required) The name of the department. 28 | * `category_id` - (Optional) The id of the category the script is under, Default: `-1`. 29 | * `priority` - (Optional) The script execution priority, Default: `AFTER`. 30 | * `info` - (Optional) The info of the script. 31 | * `notes` - (Optional) The notes of the script. 32 | * `parameter4` - (Optional) The name of parameter 4. 33 | * `parameter5` - (Optional) The name of parameter 5. 34 | * `parameter6` - (Optional) The name of parameter 6. 35 | * `parameter7` - (Optional) The name of parameter 7. 36 | * `parameter8` - (Optional) The name of parameter 8. 37 | * `parameter9` - (Optional) The name of parameter 9. 38 | * `parameter10` - (Optional) The name of parameter 10. 39 | * `os_requirements` - (Optional) The OS requirements of the script. 40 | * `script_contents` - (Optional) The contents of the script if defined in the resources declaration. 41 | 42 | ## Attributes Reference 43 | 44 | In addition to the above arguments, the following attributes are exported: 45 | 46 | * `id` - The ID of the smart computer group. 47 | * `category_name` - The name of the category the script is under. 48 | -------------------------------------------------------------------------------- /jamf/data_source_jamf_building.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 8 | "github.com/sioncojp/go-jamf-api" 9 | ) 10 | 11 | func dataSourceJamfBuilding() *schema.Resource { 12 | return &schema.Resource{ 13 | ReadContext: dataSourceJamfBuildingRead, 14 | Schema: map[string]*schema.Schema{ 15 | "name": { 16 | Type: schema.TypeString, 17 | Required: true, 18 | }, 19 | "street_address1": { 20 | Type: schema.TypeString, 21 | Computed: true, 22 | }, 23 | "street_address2": { 24 | Type: schema.TypeString, 25 | Computed: true, 26 | }, 27 | "city": { 28 | Type: schema.TypeString, 29 | Computed: true, 30 | }, 31 | "state_province": { 32 | Type: schema.TypeString, 33 | Computed: true, 34 | }, 35 | "zip_postal_code": { 36 | Type: schema.TypeString, 37 | Computed: true, 38 | }, 39 | "country": { 40 | Type: schema.TypeString, 41 | Computed: true, 42 | }, 43 | }, 44 | } 45 | } 46 | 47 | func dataSourceJamfBuildingRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 48 | var diags diag.Diagnostics 49 | c := m.(*jamf.Client) 50 | 51 | resp, err := c.GetBuildingByName(d.Get("name").(string)) 52 | if err != nil { 53 | return diag.FromErr(err) 54 | } 55 | 56 | d.SetId(resp.GetId()) 57 | d.Set("name", resp.GetName()) 58 | d.Set("street_address1", resp.GetStreetAddress1()) 59 | d.Set("street_address2", resp.GetStreetAddress2()) 60 | d.Set("city", resp.GetCity()) 61 | d.Set("state_province", resp.GetStateProvince()) 62 | d.Set("zip_postal_code", resp.GetZipPostalCode()) 63 | d.Set("country", resp.GetCountry()) 64 | 65 | return diags 66 | } 67 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # From https://github.com/hashicorp/terraform-provider-scaffolding/blob/master/.goreleaser.yml 2 | # Visit https://goreleaser.com for documentation on how to customize this 3 | # behavior. 4 | before: 5 | hooks: 6 | # this is just an example and not a requirement for provider building/publishing 7 | - go mod tidy 8 | builds: 9 | - env: 10 | # goreleaser does not work with CGO, it could also complicate 11 | # usage by users in CI/CD systems like Terraform Cloud where 12 | # they are unable to install libraries. 13 | - CGO_ENABLED=0 14 | mod_timestamp: '{{ .CommitTimestamp }}' 15 | flags: 16 | - -trimpath 17 | ldflags: 18 | - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' 19 | goos: 20 | - windows 21 | - linux 22 | - darwin 23 | goarch: 24 | - amd64 25 | - '386' 26 | - arm 27 | - arm64 28 | ignore: 29 | - goos: darwin 30 | goarch: '386' 31 | binary: '{{ .ProjectName }}_v{{ .Version }}' 32 | archives: 33 | - format: zip 34 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' 35 | checksum: 36 | name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' 37 | algorithm: sha256 38 | signs: 39 | - artifacts: checksum 40 | args: 41 | # if you are using this is a GitHub action or some other automated pipeline, you 42 | # need to pass the batch flag to indicate its not interactive. 43 | - "--batch" 44 | - "--local-user" 45 | - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key 46 | - "--output" 47 | - "${signature}" 48 | - "--detach-sign" 49 | - "${artifact}" 50 | release: 51 | # If you want to manually examine the release before its live, uncomment this line: 52 | # draft: true 53 | changelog: 54 | skip: true 55 | -------------------------------------------------------------------------------- /website/docs/r/smartComputerGroup.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "jamf" 3 | subcategory: "Resources" 4 | page_title: "Jamf: jamf_smartComputerGroup" 5 | description: |- 6 | Provides a smart computer group. 7 | --- 8 | 9 | # Resource: jamf_smartComputerGroup 10 | 11 | Provides a smart computer group. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | resource "jamf_smartComputerGroup" "test_smart_1" { 17 | name = "Test Smart 1" 18 | criteria { 19 | priority = 0 20 | name = "UDID" 21 | search_type = "is not" 22 | search_value = "FAKE-UDID-THAT-ALSO-DOES-NOT-EXIST" 23 | } 24 | criteria { 25 | priority = 1 26 | name = "UDID" 27 | search_type = "is not" 28 | search_value = "FAKE-UDID-THAT-DOES-NOT-EXIST-LIKE-REALLY" 29 | } 30 | } 31 | ``` 32 | 33 | ## Argument Reference 34 | 35 | The following arguments are supported: 36 | 37 | * `name` - (Required) The name of the group. 38 | * `criteria` - (Optional) List of computers to to be in the group. 39 | * `priority` - (Required) Priority of the criteria to be evaluated, must start at zero and increase by one. 40 | * `and_or` - (Optional) Join your search with `and` or `or`. Default: `and` 41 | * `name` - (Required) Name of the criteria to compare with. 42 | * `search_type` - (Required) The search comparator to use. 43 | * `search_value` - (Required) The search value to compare against. 44 | * `opening_paren` - (Optional) If the opening parenthese is enabled. Default `false` 45 | * `closing_paren` - (Optional) If the closing parenthese is enabled. Default `false` 46 | * `site` - (Optional) Site the computer group should be a member of. 47 | * `name` - (Required) Name of the site. 48 | 49 | ## Attributes Reference 50 | 51 | In addition to the above arguments, the following attributes are exported: 52 | 53 | * `id` - The ID of the smart computer group. 54 | -------------------------------------------------------------------------------- /website/docs/r/script.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "jamf" 3 | subcategory: "Resources" 4 | page_title: "Jamf: jamf_script" 5 | description: |- 6 | Provides a script. 7 | --- 8 | 9 | # Resource: jamf_script 10 | 11 | Provides a script. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | resource "jamf_script" "test_script_1" { 17 | name = "Test Script 1" 18 | file_path = "example_script-v1.0.0.sh" 19 | } 20 | ``` 21 | 22 | ## Argument Reference 23 | 24 | The following arguments are supported: 25 | 26 | * `name` - (Required) The name of the department. 27 | * `category_id` - (Optional) The id of the category the script is under, Default: `-1`. 28 | * `priority` - (Optional) The script execution priority, Default: `AFTER`. 29 | * `info` - (Optional) The info of the script. 30 | * `notes` - (Optional) The notes of the script. 31 | * `parameter4` - (Optional) The name of parameter 4. 32 | * `parameter5` - (Optional) The name of parameter 5. 33 | * `parameter6` - (Optional) The name of parameter 6. 34 | * `parameter7` - (Optional) The name of parameter 7. 35 | * `parameter8` - (Optional) The name of parameter 8. 36 | * `parameter9` - (Optional) The name of parameter 9. 37 | * `parameter10` - (Optional) The name of parameter 10. 38 | * `os_requirements` - (Optional) The OS requirements of the script. 39 | * `script_contents` - (Optional) The contents of the script if defined in the resources declaration. 40 | * `file_path` - (Optional) The path of a file to be read in as the script contents. 41 | 42 | ## Attributes Reference 43 | 44 | In addition to the above arguments, the following attributes are exported: 45 | 46 | * `id` - The ID of the smart computer group. 47 | * `category_name` - The name of the category the script is under. 48 | 49 | ## Notes 50 | 51 | When using the `file_path` parameter the file name must be changed to trigger a terraform update. 52 | -------------------------------------------------------------------------------- /jamf/data_source_jamf_staticComputerGroup.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 8 | "github.com/sioncojp/go-jamf-api" 9 | ) 10 | 11 | func dataSourceJamfStaticComputerGroup() *schema.Resource { 12 | return &schema.Resource{ 13 | ReadContext: dataSourceJamfStaticComputerGroupRead, 14 | Schema: map[string]*schema.Schema{ 15 | "name": { 16 | Type: schema.TypeString, 17 | Required: true, 18 | }, 19 | "id": { 20 | Type: schema.TypeInt, 21 | Computed: true, 22 | }, 23 | "site": { 24 | Type: schema.TypeSet, 25 | Computed: true, 26 | Elem: &schema.Resource{ 27 | Schema: map[string]*schema.Schema{ 28 | "id": { 29 | Type: schema.TypeInt, 30 | Computed: true, 31 | }, 32 | "name": { 33 | Type: schema.TypeString, 34 | Computed: true, 35 | }, 36 | }, 37 | }, 38 | }, 39 | "computer": { 40 | Type: schema.TypeSet, 41 | Computed: true, 42 | Elem: &schema.Resource{ 43 | Schema: map[string]*schema.Schema{ 44 | "id": { 45 | Type: schema.TypeInt, 46 | Computed: true, 47 | }, 48 | "name": { 49 | Type: schema.TypeString, 50 | Computed: true, 51 | }, 52 | "serial_number": { 53 | Type: schema.TypeString, 54 | Computed: true, 55 | }, 56 | }, 57 | }, 58 | }, 59 | }, 60 | } 61 | } 62 | 63 | func dataSourceJamfStaticComputerGroupRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 64 | var diags diag.Diagnostics 65 | c := m.(*jamf.Client) 66 | 67 | resp, err := c.GetComputerGroupByName(d.Get("name").(string)) 68 | if err != nil { 69 | return diag.FromErr(err) 70 | } 71 | 72 | if resp.IsSmart { 73 | return diags 74 | } 75 | 76 | deconstructJamfComputerGroupStruct(d, resp) 77 | 78 | return diags 79 | } 80 | -------------------------------------------------------------------------------- /website/docs/index.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "jamf" 3 | page_title: "Provider: Jamf" 4 | description: |- 5 | The Jamf provider is used to interact with the many resources supported by Jamf. The provider needs to be configured with the proper credentials before it can be used. 6 | --- 7 | 8 | # Jamf Provider 9 | 10 | The Jamf provider is used to interact with the 11 | many resources supported by Jamf. The provider needs to be configured 12 | with the proper credentials before it can be used. 13 | 14 | Use the navigation to the left to read about the available resources. 15 | 16 | ## Example Usage 17 | 18 | Terraform 0.13 and later: 19 | 20 | ```hcl 21 | terraform { 22 | required_providers { 23 | sioncojp/jamf = {} 24 | } 25 | } 26 | 27 | provider "jamf" { 28 | username = "xxxx" 29 | password = "xxxx" 30 | 31 | # "This is the full url of jamf, xxxx.jamfcloud.com" 32 | url = "xxxx" 33 | } 34 | 35 | data "jamf_department" "example" { 36 | name = "hoge" 37 | } 38 | ``` 39 | 40 | ## Authentication 41 | 42 | The Jamf provider offers a flexible means of providing credentials for 43 | authentication. The following methods are supported, in this order, and 44 | explained below: 45 | 46 | - Static credentials 47 | - Environment variables 48 | 49 | ### Static Credentials 50 | 51 | !> **Warning:** Hard-coded credentials are not recommended in any Terraform 52 | configuration and risks secret leakage should this file ever be committed to a 53 | public version control system. 54 | 55 | Static credentials can be provided by adding an `username`, `password` and `url` 56 | in-line in the Jamf provider block: 57 | 58 | Usage: 59 | 60 | ```hcl 61 | provider "jamf" { 62 | username = "xxxx" 63 | password = "xxxx" 64 | 65 | # "This is the xxxx part of xxxx.jamfcloud.com" 66 | url = "xxxx" 67 | } 68 | ``` 69 | 70 | ### Environment Variables 71 | 72 | You can provide your credentials via the `JAMF_USERNAME`, `JAMF_PASSWORD` and 73 | `JAMF_URL`, environment variables. 74 | 75 | ```hcl 76 | provider "jamf" {} 77 | ``` 78 | 79 | Usage: 80 | 81 | ```sh 82 | $ export JAMF_USERNAME="xxxx" 83 | $ export JAMF_PASSWORD="xxxx" 84 | $ export JAMF_URL="xxxx" 85 | $ terraform plan 86 | ``` 87 | 88 | ## Argument Reference 89 | 90 | In addition to [generic `provider` arguments](https://www.terraform.io/docs/configuration/providers.html) 91 | (e.g. `alias` and `version`), the following arguments are supported in the Jamf 92 | `provider` block: 93 | 94 | * `username` - (Optional) This is the Jamf username. 95 | 96 | * `password` - (Optional) This is the Jamf user password. 97 | 98 | * `url` - (Optional) This is the Jamf server url. 99 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: terraform/* build fmtcheck test testacc 2 | 3 | ##### Terraform 4 | OS_TYPE := $(shell echo $(shell uname) | tr A-Z a-z) 5 | OS_ARCH := amd64 6 | OS_BUILD := ${OS_TYPE}_amd64 7 | BINDIR := ~/bin 8 | 9 | TERRAFORM := ~/bin/terraform 10 | TERRAFORM_VERSION := 0.13.4 11 | 12 | ### install 13 | terraform/install: file = terraform_$(TERRAFORM_VERSION)_$(OS_TYPE)_$(OS_ARCH).zip 14 | terraform/install: download_url = https://releases.hashicorp.com/terraform/$(TERRAFORM_VERSION)/$(file) 15 | terraform/install: 16 | rm -f ${TERRAFORM} 17 | curl -L -fsS --retry 2 -o $(BINDIR)/$(file) $(download_url) && \ 18 | unzip -qq $(BINDIR)/$(file) -d $(BINDIR) && rm -f $(BINDIR)/$(file) 19 | 20 | 21 | ##### Go 22 | NAME := $(notdir $(PWD)) 23 | VERSION := 1.0.0 24 | TEST ?= $$(go list ./... |grep -v 'vendor') 25 | 26 | build: ## go build 27 | CGO_ENABLED=0 go build -o $(BINDIR)/$(NAME)_v$(VERSION) main.go 28 | 29 | install: build 30 | mkdir -p ${BINDIR}/plugins/registry.terraform.io/hashicorp/jamf/${VERSION}/${OS_BUILD} 31 | mv $(BINDIR)/$(NAME)_v$(VERSION) ${BINDIR}/plugins/registry.terraform.io/hashicorp/jamf/${VERSION}/${OS_BUILD} 32 | 33 | release: 34 | mkdir -p ${BINDIR}/release/${VERSION} 35 | GOOS=darwin GOARCH=amd64 go build -o ${BINDIR}/release/${VERSION}/${NAME}_${VERSION}_darwin_amd64 36 | GOOS=freebsd GOARCH=386 go build -o ${BINDIR}/release/${VERSION}/${NAME}_${VERSION}_freebsd_386 37 | GOOS=freebsd GOARCH=amd64 go build -o ${BINDIR}/release/${VERSION}/${NAME}_${VERSION}_freebsd_amd64 38 | GOOS=freebsd GOARCH=arm go build -o ${BINDIR}/release/${VERSION}/${NAME}_${VERSION}_freebsd_arm 39 | GOOS=linux GOARCH=386 go build -o ${BINDIR}/release/${VERSION}/${NAME}_${VERSION}_linux_386 40 | GOOS=linux GOARCH=amd64 go build -o ${BINDIR}/release/${VERSION}/${NAME}_${VERSION}_linux_amd64 41 | GOOS=linux GOARCH=arm go build -o ${BINDIR}/release/${VERSION}/${NAME}_${VERSION}_linux_arm 42 | GOOS=openbsd GOARCH=386 go build -o ${BINDIR}/release/${VERSION}/${NAME}_${VERSION}_openbsd_386 43 | GOOS=openbsd GOARCH=amd64 go build -o ${BINDIR}/release/${VERSION}/${NAME}_${VERSION}_openbsd_amd64 44 | GOOS=solaris GOARCH=amd64 go build -o ${BINDIR}/release/${VERSION}/${NAME}_${VERSION}_solaris_amd64 45 | GOOS=windows GOARCH=386 go build -o ${BINDIR}/release/${VERSION}/${NAME}_${VERSION}_windows_386 46 | GOOS=windows GOARCH=amd64 go build -o ${BINDIR}/release/${VERSION}/${NAME}_${VERSION}_windows_amd64 47 | 48 | test: 49 | go test $(TEST) -v -timeout=5m -parallel=4 50 | 51 | testacc: fmtcheck 52 | TF_ACC=1 go test ./jamf -v -count 1 -parallel 20 $(TESTARGS) -timeout 120m 53 | 54 | fmtcheck: 55 | @sh -c "'$(CURDIR)/scripts/gofmtcheck.sh'" 56 | -------------------------------------------------------------------------------- /jamf/provider.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/go-cleanhttp" 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 9 | "github.com/sioncojp/go-jamf-api" 10 | ) 11 | 12 | type ProviderConfiguration struct { 13 | Client *jamf.Client 14 | } 15 | 16 | // Provider ... Define variables for the provider to use in the .tf file 17 | func Provider() *schema.Provider { 18 | provider := &schema.Provider{ 19 | Schema: map[string]*schema.Schema{ 20 | "username": { 21 | Type: schema.TypeString, 22 | Optional: true, 23 | DefaultFunc: schema.MultiEnvDefaultFunc([]string{"JAMF_USERNAME"}, nil), 24 | }, 25 | "password": { 26 | Type: schema.TypeString, 27 | Optional: true, 28 | DefaultFunc: schema.MultiEnvDefaultFunc([]string{"JAMF_PASSWORD"}, nil), 29 | }, 30 | "url": { 31 | Type: schema.TypeString, 32 | Optional: true, 33 | Description: "This is the full url of jamf, xxxx.jamfcloud.com", 34 | DefaultFunc: schema.MultiEnvDefaultFunc([]string{"JAMF_URL"}, nil), 35 | }, 36 | }, 37 | 38 | ResourcesMap: map[string]*schema.Resource{ 39 | "jamf_department": resourceJamfDepartment(), 40 | "jamf_category": resourceJamfCategory(), 41 | "jamf_building": resourceJamfBuilding(), 42 | "jamf_staticComputerGroup": resourceJamfStaticComputerGroup(), 43 | "jamf_smartComputerGroup": resourceJamfSmartComputerGroup(), 44 | "jamf_script": resourceJamfScript(), 45 | "jamf_policy": resourceJamfPolicy(), 46 | }, 47 | 48 | DataSourcesMap: map[string]*schema.Resource{ 49 | "jamf_department": dataSourceJamfDepartment(), 50 | "jamf_category": dataSourceJamfCategory(), 51 | "jamf_building": dataSourceJamfBuilding(), 52 | "jamf_staticComputerGroup": dataSourceJamfStaticComputerGroup(), 53 | "jamf_smartComputerGroup": dataSourceJamfSmartComputerGroup(), 54 | "jamf_script": dataSourceJamfScript(), 55 | "jamf_package": dataSourceJamfPackage(), 56 | "jamf_policy": dataSourceJamfPolicy(), 57 | }, 58 | ConfigureContextFunc: providerConfigure, 59 | } 60 | 61 | return provider 62 | } 63 | 64 | func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { 65 | var diags diag.Diagnostics 66 | c, err := jamf.NewClient(d.Get("username").(string), d.Get("password").(string), d.Get("url").(string)) 67 | if err != nil { 68 | diag.FromErr(err) 69 | } 70 | c.ExtraHeader["User-Agent"] = AppName 71 | c.HttpClient = cleanhttp.DefaultClient() 72 | return c, diags 73 | } 74 | -------------------------------------------------------------------------------- /jamf/data_source_jamf_packages.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 9 | "github.com/sioncojp/go-jamf-api" 10 | ) 11 | 12 | func dataSourceJamfPackage() *schema.Resource { 13 | return &schema.Resource{ 14 | ReadContext: dataSourceJamfPackageRead, 15 | Schema: map[string]*schema.Schema{ 16 | "name": { 17 | Type: schema.TypeString, 18 | Required: true, 19 | }, 20 | "id": { 21 | Type: schema.TypeInt, 22 | Computed: true, 23 | }, 24 | "category_name": { 25 | Type: schema.TypeString, 26 | Computed: true, 27 | }, 28 | "filename": { 29 | Type: schema.TypeString, 30 | Computed: true, 31 | }, 32 | "notes": { 33 | Type: schema.TypeString, 34 | Computed: true, 35 | }, 36 | "priority": { 37 | Type: schema.TypeInt, 38 | Computed: true, 39 | }, 40 | "fill_existing_users": { 41 | Type: schema.TypeBool, 42 | Computed: true, 43 | }, 44 | "boot_volume_required": { 45 | Type: schema.TypeBool, 46 | Computed: true, 47 | }, 48 | "allow_uninstalled": { 49 | Type: schema.TypeBool, 50 | Computed: true, 51 | }, 52 | "os_requirements": { 53 | Type: schema.TypeString, 54 | Computed: true, 55 | }, 56 | "required_processor": { 57 | Type: schema.TypeString, 58 | Computed: true, 59 | }, 60 | "hash_type": { 61 | Type: schema.TypeString, 62 | Computed: true, 63 | }, 64 | "hash_value": { 65 | Type: schema.TypeString, 66 | Computed: true, 67 | }, 68 | }, 69 | } 70 | } 71 | 72 | func dataSourceJamfPackageRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 73 | var diags diag.Diagnostics 74 | c := m.(*jamf.Client) 75 | 76 | resp, err := c.GetPackageByName(d.Get("name").(string)) 77 | if err != nil { 78 | return diag.FromErr(err) 79 | } 80 | 81 | deconstructPackageStruct(d, resp) 82 | 83 | return diags 84 | } 85 | 86 | func deconstructPackageStruct(d *schema.ResourceData, in *jamf.Package) { 87 | d.SetId(strconv.Itoa(in.ID)) 88 | d.Set("name", in.Name) 89 | d.Set("category_name", in.CategoryName) 90 | d.Set("filename", in.Filename) 91 | d.Set("info", in.Info) 92 | d.Set("notes", in.Notes) 93 | d.Set("priority", in.Priority) 94 | d.Set("reboot_required", in.RebootRequired) 95 | d.Set("fill_existing_users", in.FillExistingUsers) 96 | d.Set("boot_volume_required", in.BootVolumeRequired) 97 | d.Set("allow_uninstalled", in.AllowUninstalled) 98 | d.Set("os_requirements", in.OsRequirements) 99 | d.Set("required_processor", in.RequiredProcessor) 100 | d.Set("hash_type", in.HashType) 101 | d.Set("hash_value", in.HashValue) 102 | return 103 | } 104 | -------------------------------------------------------------------------------- /jamf/data_source_jamf_script.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 8 | "github.com/sioncojp/go-jamf-api" 9 | ) 10 | 11 | func dataSourceJamfScript() *schema.Resource { 12 | return &schema.Resource{ 13 | ReadContext: dataSourceJamfScriptRead, 14 | Schema: map[string]*schema.Schema{ 15 | "name": { 16 | Type: schema.TypeString, 17 | Required: true, 18 | }, 19 | "id": { 20 | Type: schema.TypeString, 21 | Computed: true, 22 | }, 23 | "category_id": { 24 | Type: schema.TypeString, 25 | Computed: true, 26 | }, 27 | "category_name": { 28 | Type: schema.TypeString, 29 | Computed: true, 30 | }, 31 | "info": { 32 | Type: schema.TypeString, 33 | Computed: true, 34 | }, 35 | "notes": { 36 | Type: schema.TypeString, 37 | Computed: true, 38 | }, 39 | "priority": { 40 | Type: schema.TypeString, 41 | Computed: true, 42 | }, 43 | "parameter4": { 44 | Type: schema.TypeString, 45 | Computed: true, 46 | }, 47 | "parameter5": { 48 | Type: schema.TypeString, 49 | Computed: true, 50 | }, 51 | "parameter6": { 52 | Type: schema.TypeString, 53 | Computed: true, 54 | }, 55 | "parameter7": { 56 | Type: schema.TypeString, 57 | Computed: true, 58 | }, 59 | "parameter8": { 60 | Type: schema.TypeString, 61 | Computed: true, 62 | }, 63 | "parameter9": { 64 | Type: schema.TypeString, 65 | Computed: true, 66 | }, 67 | "parameter10": { 68 | Type: schema.TypeString, 69 | Computed: true, 70 | }, 71 | "parameter11": { 72 | Type: schema.TypeString, 73 | Computed: true, 74 | }, 75 | "os_requirements": { 76 | Type: schema.TypeString, 77 | Computed: true, 78 | }, 79 | "script_contents": { 80 | Type: schema.TypeString, 81 | Computed: true, 82 | }, 83 | }, 84 | } 85 | } 86 | 87 | func dataSourceJamfScriptRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 88 | var diags diag.Diagnostics 89 | c := m.(*jamf.Client) 90 | 91 | resp, err := c.GetScriptByName(d.Get("name").(string)) 92 | if err != nil { 93 | return diag.FromErr(err) 94 | } 95 | 96 | deconstructJamfScriptStruct(d, resp) 97 | 98 | return diags 99 | } 100 | 101 | func deconstructJamfScriptStruct(d *schema.ResourceData, in *jamf.Script) { 102 | d.SetId(in.ID) 103 | d.Set("name", in.Name) 104 | d.Set("category_id", in.CategoryID) 105 | d.Set("category_name", in.CategoryName) 106 | d.Set("info", in.Info) 107 | d.Set("notes", in.Notes) 108 | d.Set("priority", in.Priority) 109 | d.Set("parameter4", in.Parameter4) 110 | d.Set("parameter5", in.Parameter5) 111 | d.Set("parameter6", in.Parameter6) 112 | d.Set("parameter7", in.Parameter7) 113 | d.Set("parameter8", in.Parameter8) 114 | d.Set("parameter9", in.Parameter9) 115 | d.Set("parameter10", in.Parameter10) 116 | d.Set("parameter11", in.Parameter11) 117 | d.Set("os_requirements", in.OsRequirements) 118 | if _, hasFilePath := d.GetOk("file_path"); !hasFilePath { 119 | d.Set("script_contents", in.ScriptContents) 120 | } 121 | 122 | return 123 | } 124 | -------------------------------------------------------------------------------- /jamf/resource_jamf_department.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | "github.com/sioncojp/go-jamf-api" 11 | ) 12 | 13 | func resourceJamfDepartment() *schema.Resource { 14 | return &schema.Resource{ 15 | CreateContext: resourceJamfDepartmentCreate, 16 | ReadContext: resourceJamfDepartmentRead, 17 | UpdateContext: resourceJamfDepartmentUpdate, 18 | DeleteContext: resourceJamfDepartmentDelete, 19 | 20 | Timeouts: &schema.ResourceTimeout{ 21 | Create: schema.DefaultTimeout(1 * time.Minute), 22 | Read: schema.DefaultTimeout(1 * time.Minute), 23 | Update: schema.DefaultTimeout(1 * time.Minute), 24 | Delete: schema.DefaultTimeout(1 * time.Minute), 25 | }, 26 | 27 | Importer: &schema.ResourceImporter{ 28 | StateContext: importJamfDepartmentState, 29 | }, 30 | Schema: map[string]*schema.Schema{ 31 | "name": { 32 | Type: schema.TypeString, 33 | Required: true, 34 | }, 35 | }, 36 | } 37 | 38 | return &schema.Resource{} 39 | } 40 | 41 | func buildJamfDepartmentStruct(d *schema.ResourceData) *jamf.Department { 42 | var out jamf.Department 43 | out.SetId(d.Id()) 44 | out.SetName(d.Get("name").(string)) 45 | 46 | return &out 47 | } 48 | 49 | func resourceJamfDepartmentCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 50 | c := m.(*jamf.Client) 51 | 52 | b := buildJamfDepartmentStruct(d) 53 | 54 | resp, err := c.CreateDepartment(b.Name) 55 | if err != nil { 56 | return diag.FromErr(err) 57 | } 58 | 59 | d.SetId(resp.GetId()) 60 | 61 | return resourceJamfDepartmentRead(ctx, d, m) 62 | } 63 | 64 | func resourceJamfDepartmentRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 65 | var diags diag.Diagnostics 66 | c := m.(*jamf.Client) 67 | 68 | resp, err := c.GetDepartmentByName(d.Get("name").(string)) 69 | if err != nil { 70 | return diag.FromErr(err) 71 | } 72 | 73 | d.Set("name", resp.GetName()) 74 | 75 | return diags 76 | } 77 | 78 | func resourceJamfDepartmentUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 79 | c := m.(*jamf.Client) 80 | 81 | b := buildJamfDepartmentStruct(d) 82 | d.SetId(b.GetId()) 83 | 84 | if _, err := c.UpdateDepartment(b); err != nil { 85 | return diag.FromErr(err) 86 | } 87 | 88 | return resourceJamfDepartmentRead(ctx, d, m) 89 | } 90 | 91 | func resourceJamfDepartmentDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 92 | var diags diag.Diagnostics 93 | c := m.(*jamf.Client) 94 | b := buildJamfDepartmentStruct(d) 95 | 96 | if err := c.DeleteDepartment(*b.Name); err != nil { 97 | return diag.FromErr(err) 98 | } 99 | 100 | d.SetId("") 101 | 102 | return diags 103 | } 104 | 105 | func importJamfDepartmentState(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { 106 | c := m.(*jamf.Client) 107 | d.SetId(d.Id()) 108 | resp, err := c.GetDepartment(d.Id()) 109 | if err != nil { 110 | return nil, fmt.Errorf("cannot get department data") 111 | } 112 | 113 | d.Set("name", resp.GetName()) 114 | 115 | return []*schema.ResourceData{d}, nil 116 | } 117 | -------------------------------------------------------------------------------- /jamf/resource_jamf_category.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | "github.com/sioncojp/go-jamf-api" 11 | ) 12 | 13 | func resourceJamfCategory() *schema.Resource { 14 | return &schema.Resource{ 15 | CreateContext: resourceJamfCategoryCreate, 16 | ReadContext: resourceJamfCategoryRead, 17 | UpdateContext: resourceJamfCategoryUpdate, 18 | DeleteContext: resourceJamfCategoryDelete, 19 | 20 | Timeouts: &schema.ResourceTimeout{ 21 | Create: schema.DefaultTimeout(1 * time.Minute), 22 | Read: schema.DefaultTimeout(1 * time.Minute), 23 | Update: schema.DefaultTimeout(1 * time.Minute), 24 | Delete: schema.DefaultTimeout(1 * time.Minute), 25 | }, 26 | 27 | Importer: &schema.ResourceImporter{ 28 | StateContext: importJamfCategoryState, 29 | }, 30 | Schema: map[string]*schema.Schema{ 31 | // Computed values. 32 | "name": { 33 | Type: schema.TypeString, 34 | Required: true, 35 | }, 36 | "priority": { 37 | Type: schema.TypeInt, 38 | Required: true, 39 | }, 40 | }, 41 | } 42 | 43 | return &schema.Resource{} 44 | } 45 | 46 | func buildJamfCategoryStruct(d *schema.ResourceData) *jamf.Category { 47 | var out jamf.Category 48 | out.SetId(d.Id()) 49 | out.SetName(d.Get("name").(string)) 50 | out.SetPriority(d.Get("priority").(int)) 51 | 52 | return &out 53 | } 54 | 55 | func resourceJamfCategoryCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 56 | c := m.(*jamf.Client) 57 | 58 | b := buildJamfCategoryStruct(d) 59 | 60 | resp, err := c.CreateCategory(b.Name, b.Priority) 61 | if err != nil { 62 | return diag.FromErr(err) 63 | } 64 | 65 | d.SetId(resp.GetId()) 66 | 67 | return resourceJamfCategoryRead(ctx, d, m) 68 | } 69 | 70 | func resourceJamfCategoryRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 71 | var diags diag.Diagnostics 72 | c := m.(*jamf.Client) 73 | 74 | resp, err := c.GetCategoryByName(d.Get("name").(string)) 75 | if err != nil { 76 | return diag.FromErr(err) 77 | } 78 | 79 | d.Set("name", resp.GetName()) 80 | d.Set("priority", resp.GetPriority()) 81 | 82 | return diags 83 | } 84 | 85 | func resourceJamfCategoryUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 86 | c := m.(*jamf.Client) 87 | 88 | b := buildJamfCategoryStruct(d) 89 | d.SetId(b.GetId()) 90 | 91 | if _, err := c.UpdateCategory(b); err != nil { 92 | return diag.FromErr(err) 93 | } 94 | 95 | return resourceJamfCategoryRead(ctx, d, m) 96 | } 97 | 98 | func resourceJamfCategoryDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 99 | var diags diag.Diagnostics 100 | c := m.(*jamf.Client) 101 | b := buildJamfCategoryStruct(d) 102 | 103 | if err := c.DeleteCategory(*b.Name); err != nil { 104 | return diag.FromErr(err) 105 | } 106 | 107 | d.SetId("") 108 | 109 | return diags 110 | } 111 | 112 | func importJamfCategoryState(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { 113 | c := m.(*jamf.Client) 114 | d.SetId(d.Id()) 115 | resp, err := c.GetCategory(d.Id()) 116 | if err != nil { 117 | return nil, fmt.Errorf("cannot get Category data") 118 | } 119 | 120 | d.Set("name", resp.GetName()) 121 | d.Set("priority", resp.GetPriority()) 122 | 123 | return []*schema.ResourceData{d}, nil 124 | } 125 | -------------------------------------------------------------------------------- /jamf/data_source_jamf_smartComputerGroup.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 9 | "github.com/sioncojp/go-jamf-api" 10 | ) 11 | 12 | func dataSourceJamfSmartComputerGroup() *schema.Resource { 13 | return &schema.Resource{ 14 | ReadContext: dataSourceJamfSmartComputerGroupRead, 15 | Schema: map[string]*schema.Schema{ 16 | "name": { 17 | Type: schema.TypeString, 18 | Required: true, 19 | }, 20 | "id": { 21 | Type: schema.TypeInt, 22 | Computed: true, 23 | }, 24 | "site": { 25 | Type: schema.TypeSet, 26 | Computed: true, 27 | Elem: &schema.Resource{ 28 | Schema: map[string]*schema.Schema{ 29 | "id": { 30 | Type: schema.TypeInt, 31 | Computed: true, 32 | }, 33 | "name": { 34 | Type: schema.TypeString, 35 | Computed: true, 36 | }, 37 | }, 38 | }, 39 | }, 40 | "criteria": { 41 | Type: schema.TypeSet, 42 | Computed: true, 43 | Elem: &schema.Resource{ 44 | Schema: map[string]*schema.Schema{ 45 | "priority": { 46 | Type: schema.TypeInt, 47 | Computed: true, 48 | }, 49 | "name": { 50 | Type: schema.TypeString, 51 | Computed: true, 52 | }, 53 | "and_or": { 54 | Type: schema.TypeString, 55 | Computed: true, 56 | }, 57 | "search_type": { 58 | Type: schema.TypeString, 59 | Computed: true, 60 | }, 61 | "search_value": { 62 | Type: schema.TypeString, 63 | Computed: true, 64 | }, 65 | "opening_paren": { 66 | Type: schema.TypeBool, 67 | Computed: true, 68 | }, 69 | "closing_paren": { 70 | Type: schema.TypeBool, 71 | Computed: true, 72 | }, 73 | }, 74 | }, 75 | }, 76 | "computer": { 77 | Type: schema.TypeSet, 78 | Computed: true, 79 | Elem: &schema.Resource{ 80 | Schema: map[string]*schema.Schema{ 81 | "id": { 82 | Type: schema.TypeInt, 83 | Computed: true, 84 | }, 85 | "name": { 86 | Type: schema.TypeString, 87 | Computed: true, 88 | }, 89 | "serial_number": { 90 | Type: schema.TypeString, 91 | Computed: true, 92 | }, 93 | }, 94 | }, 95 | }, 96 | }, 97 | } 98 | } 99 | 100 | func dataSourceJamfSmartComputerGroupRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 101 | var diags diag.Diagnostics 102 | c := m.(*jamf.Client) 103 | 104 | resp, err := c.GetComputerGroupByName(d.Get("name").(string)) 105 | if err != nil { 106 | return diag.FromErr(err) 107 | } 108 | 109 | if !resp.IsSmart { 110 | return diags 111 | } 112 | 113 | deconstructJamfComputerGroupStruct(d, resp) 114 | 115 | return diags 116 | } 117 | 118 | func deconstructJamfComputerGroupStruct(d *schema.ResourceData, in *jamf.ComputerGroup) { 119 | d.SetId(strconv.Itoa(in.ID)) 120 | d.Set("name", in.Name) 121 | 122 | if in.Site.ID != -1 { 123 | d.Set("site", []interface{}{ 124 | map[string]interface{}{ 125 | "id": in.Site.ID, 126 | "name": in.Site.Name, 127 | }, 128 | }) 129 | } 130 | 131 | criterias := make([]interface{}, len(in.Criteria), len(in.Criteria)) 132 | for i, v := range in.Criteria { 133 | criterias[i] = map[string]interface{}{ 134 | "priority": v.Priority, 135 | "name": v.Name, 136 | "and_or": v.AndOr, 137 | "search_type": v.SearchType, 138 | "search_value": v.SearchValue, 139 | "opening_paren": v.OpeningParen, 140 | "closing_paren": v.ClosingParen, 141 | } 142 | } 143 | d.Set("criteria", criterias) 144 | 145 | comps := make([]interface{}, len(in.Computers), len(in.Computers)) 146 | for i, v := range in.Computers { 147 | comps[i] = map[string]interface{}{ 148 | "id": v.ID, 149 | "name": v.Name, 150 | "serial_number": v.SerialNumber, 151 | } 152 | } 153 | d.Set("computer", comps) 154 | 155 | return 156 | } 157 | -------------------------------------------------------------------------------- /jamf/resource_jamf_building.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | "github.com/sioncojp/go-jamf-api" 11 | ) 12 | 13 | func resourceJamfBuilding() *schema.Resource { 14 | return &schema.Resource{ 15 | CreateContext: resourceJamfBuildingCreate, 16 | ReadContext: resourceJamfBuildingRead, 17 | UpdateContext: resourceJamfBuildingUpdate, 18 | DeleteContext: resourceJamfBuildingDelete, 19 | 20 | Timeouts: &schema.ResourceTimeout{ 21 | Create: schema.DefaultTimeout(1 * time.Minute), 22 | Read: schema.DefaultTimeout(1 * time.Minute), 23 | Update: schema.DefaultTimeout(1 * time.Minute), 24 | Delete: schema.DefaultTimeout(1 * time.Minute), 25 | }, 26 | 27 | Importer: &schema.ResourceImporter{ 28 | StateContext: importJamfBuildingState, 29 | }, 30 | Schema: map[string]*schema.Schema{ 31 | "name": { 32 | Type: schema.TypeString, 33 | Required: true, 34 | }, 35 | "street_address1": { 36 | Type: schema.TypeString, 37 | Optional: true, 38 | }, 39 | "street_address2": { 40 | Type: schema.TypeString, 41 | Optional: true, 42 | }, 43 | "city": { 44 | Type: schema.TypeString, 45 | Optional: true, 46 | }, 47 | "state_province": { 48 | Type: schema.TypeString, 49 | Optional: true, 50 | }, 51 | "zip_postal_code": { 52 | Type: schema.TypeString, 53 | Optional: true, 54 | }, 55 | "country": { 56 | Type: schema.TypeString, 57 | Optional: true, 58 | }, 59 | }, 60 | } 61 | 62 | return &schema.Resource{} 63 | } 64 | 65 | func buildJamfBuildingStruct(d *schema.ResourceData) *jamf.Building { 66 | var out jamf.Building 67 | out.SetId(d.Id()) 68 | out.SetName(d.Get("name").(string)) 69 | out.SetStreetAddress1(d.Get("street_address1").(string)) 70 | out.SetStreetAddress2(d.Get("street_address2").(string)) 71 | out.SetCity(d.Get("city").(string)) 72 | out.SetStateProvince(d.Get("state_province").(string)) 73 | out.SetZipPostalCode(d.Get("zip_postal_code").(string)) 74 | out.SetCountry(d.Get("country").(string)) 75 | 76 | return &out 77 | } 78 | 79 | func resourceJamfBuildingCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 80 | c := m.(*jamf.Client) 81 | 82 | b := buildJamfBuildingStruct(d) 83 | 84 | out, err := c.CreateBuilding(b.Name, b.StreetAddress1, b.StreetAddress2, b.City, b.StateProvince, b.ZipPostalCode, b.Country) 85 | if err != nil { 86 | return diag.FromErr(err) 87 | } 88 | 89 | d.SetId(out.GetId()) 90 | 91 | return resourceJamfBuildingRead(ctx, d, m) 92 | } 93 | 94 | func resourceJamfBuildingRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 95 | var diags diag.Diagnostics 96 | c := m.(*jamf.Client) 97 | 98 | resp, err := c.GetBuildingByName(d.Get("name").(string)) 99 | if err != nil { 100 | return diag.FromErr(err) 101 | } 102 | 103 | d.Set("name", resp.GetName()) 104 | d.Set("street_address1", resp.GetStreetAddress1()) 105 | d.Set("street_address2", resp.GetStreetAddress2()) 106 | d.Set("city", resp.GetCity()) 107 | d.Set("state_province", resp.GetStateProvince()) 108 | d.Set("zip_postal_code", resp.GetZipPostalCode()) 109 | d.Set("country", resp.GetCountry()) 110 | 111 | return diags 112 | } 113 | 114 | func resourceJamfBuildingUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 115 | c := m.(*jamf.Client) 116 | 117 | b := buildJamfBuildingStruct(d) 118 | d.SetId(b.GetId()) 119 | 120 | if _, err := c.UpdateBuilding(b); err != nil { 121 | return diag.FromErr(err) 122 | } 123 | 124 | return resourceJamfBuildingRead(ctx, d, m) 125 | } 126 | 127 | func resourceJamfBuildingDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 128 | var diags diag.Diagnostics 129 | c := m.(*jamf.Client) 130 | b := buildJamfBuildingStruct(d) 131 | 132 | if err := c.DeleteBuilding(*b.Name); err != nil { 133 | return diag.FromErr(err) 134 | } 135 | 136 | d.SetId("") 137 | 138 | return diags 139 | } 140 | 141 | func importJamfBuildingState(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { 142 | c := m.(*jamf.Client) 143 | d.SetId(d.Id()) 144 | resp, err := c.GetBuilding(d.Id()) 145 | if err != nil { 146 | return nil, fmt.Errorf("cannot get Building data") 147 | } 148 | 149 | d.Set("name", resp.GetName()) 150 | d.Set("street_address1", resp.GetStreetAddress1()) 151 | d.Set("street_address2", resp.GetStreetAddress2()) 152 | d.Set("city", resp.GetCity()) 153 | d.Set("state_province", resp.GetStateProvince()) 154 | d.Set("zip_postal_code", resp.GetZipPostalCode()) 155 | d.Set("country", resp.GetCountry()) 156 | 157 | return []*schema.ResourceData{d}, nil 158 | } 159 | -------------------------------------------------------------------------------- /jamf/resource_jamf_staticComputerGroup.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strconv" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | "github.com/sioncojp/go-jamf-api" 11 | ) 12 | 13 | func resourceJamfStaticComputerGroup() *schema.Resource { 14 | return &schema.Resource{ 15 | CreateContext: resourceJamfStaticComputerGroupCreate, 16 | ReadContext: resourceJamfStaticComputerGroupRead, 17 | UpdateContext: resourceJamfStaticComputerGroupUpdate, 18 | DeleteContext: resourceJamfStaticComputerGroupDelete, 19 | Importer: &schema.ResourceImporter{ 20 | StateContext: importJamfStaticComputerGroupState, 21 | }, 22 | Schema: map[string]*schema.Schema{ 23 | "name": { 24 | Type: schema.TypeString, 25 | Required: true, 26 | }, 27 | "id": { 28 | Type: schema.TypeString, 29 | Computed: true, 30 | }, 31 | "site": { 32 | Type: schema.TypeSet, 33 | Optional: true, 34 | MaxItems: 1, 35 | Elem: &schema.Resource{ 36 | Schema: map[string]*schema.Schema{ 37 | "id": { 38 | Type: schema.TypeInt, 39 | Computed: true, 40 | }, 41 | "name": { 42 | Type: schema.TypeString, 43 | Required: true, 44 | }, 45 | }, 46 | }, 47 | }, 48 | "computer": { 49 | Type: schema.TypeSet, 50 | Optional: true, 51 | Elem: &schema.Resource{ 52 | Schema: map[string]*schema.Schema{ 53 | "id": { 54 | Type: schema.TypeInt, 55 | Optional: true, 56 | }, 57 | "name": { 58 | Type: schema.TypeString, 59 | Computed: true, 60 | }, 61 | "serial_number": { 62 | Type: schema.TypeString, 63 | Computed: true, 64 | }, 65 | }, 66 | }, 67 | }, 68 | }, 69 | } 70 | } 71 | 72 | func buildJamfStaticComputerGroupStruct(d *schema.ResourceData) *jamf.ComputerGroup { 73 | var out jamf.ComputerGroup 74 | id, _ := strconv.Atoi(d.Id()) 75 | out.ID = id 76 | out.Name = d.Get("name").(string) 77 | out.IsSmart = false 78 | if v, ok := d.GetOk("site"); ok { 79 | siteList := v.(*schema.Set).List() 80 | site := siteList[0].(map[string]interface{}) 81 | if val, ok := site["name"].(string); ok { 82 | out.Site.Name = val 83 | } 84 | if val, ok := site["id"].(int); ok { 85 | out.Site.ID = val 86 | } 87 | } 88 | 89 | if v, ok := d.GetOk("computer"); ok { 90 | comps := v.(*schema.Set).List() 91 | compList := []jamf.Computer{} 92 | for _, c := range comps { 93 | compData := c.(map[string]interface{}) 94 | comp := jamf.Computer{} 95 | if val, ok := compData["id"].(int); ok { 96 | comp.ID = val 97 | } 98 | if val, ok := compData["serial_number"].(string); ok { 99 | comp.SerialNumber = val 100 | } 101 | if val, ok := compData["name"].(string); ok { 102 | comp.Name = val 103 | } 104 | compList = append(compList, comp) 105 | } 106 | out.Computers = compList 107 | } 108 | 109 | return &out 110 | } 111 | 112 | func resourceJamfStaticComputerGroupCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 113 | c := m.(*jamf.Client) 114 | 115 | b := buildJamfStaticComputerGroupStruct(d) 116 | 117 | group := &jamf.ComputerGroupRequest{} 118 | group.Name = b.Name 119 | if b.Site.Name != "" { 120 | group.Site = b.Site 121 | } 122 | group.IsSmart = b.IsSmart 123 | group.Computers = b.Computers 124 | 125 | id, err := c.CreateComputerGroup(group) 126 | if err != nil { 127 | return diag.FromErr(err) 128 | } 129 | 130 | d.SetId(strconv.Itoa(id)) 131 | 132 | return resourceJamfStaticComputerGroupRead(ctx, d, m) 133 | } 134 | 135 | func resourceJamfStaticComputerGroupRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 136 | var diags diag.Diagnostics 137 | c := m.(*jamf.Client) 138 | 139 | id, err := strconv.Atoi(d.Id()) 140 | if err != nil { 141 | return diag.FromErr(err) 142 | } 143 | resp, err := c.GetComputerGroup(id) 144 | if err != nil { 145 | if jamfErr, ok := err.(jamf.Error); ok && jamfErr.StatusCode() == 404 { 146 | d.SetId("") 147 | } else { 148 | return diag.FromErr(err) 149 | } 150 | } else { 151 | deconstructJamfComputerGroupStruct(d, resp) 152 | } 153 | 154 | return diags 155 | } 156 | 157 | func resourceJamfStaticComputerGroupUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 158 | c := m.(*jamf.Client) 159 | 160 | b := buildJamfStaticComputerGroupStruct(d) 161 | 162 | if _, err := c.UpdateComputerGroup(b); err != nil { 163 | return diag.FromErr(err) 164 | } 165 | 166 | return resourceJamfStaticComputerGroupRead(ctx, d, m) 167 | } 168 | 169 | func resourceJamfStaticComputerGroupDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 170 | var diags diag.Diagnostics 171 | c := m.(*jamf.Client) 172 | b := buildJamfStaticComputerGroupStruct(d) 173 | 174 | if _, err := c.DeleteComputerGroup(b.ID); err != nil { 175 | return diag.FromErr(err) 176 | } 177 | 178 | d.SetId("") 179 | 180 | return diags 181 | } 182 | 183 | func importJamfStaticComputerGroupState(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { 184 | c := m.(*jamf.Client) 185 | d.SetId(d.Id()) 186 | id, err := strconv.Atoi(d.Id()) 187 | if err != nil { 188 | return nil, err 189 | } 190 | resp, err := c.GetComputerGroup(id) 191 | if err != nil { 192 | return nil, fmt.Errorf("cannot get Computer Group data") 193 | } 194 | 195 | deconstructJamfComputerGroupStruct(d, resp) 196 | 197 | return []*schema.ResourceData{d}, nil 198 | } 199 | -------------------------------------------------------------------------------- /jamf/resource_jamf_script.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "context" 5 | "io/ioutil" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 9 | "github.com/mitchellh/go-homedir" 10 | "github.com/sioncojp/go-jamf-api" 11 | ) 12 | 13 | func resourceJamfScript() *schema.Resource { 14 | return &schema.Resource{ 15 | CreateContext: resourceJamfScriptCreate, 16 | ReadContext: resourceJamfScriptRead, 17 | UpdateContext: resourceJamfScriptUpdate, 18 | DeleteContext: resourceJamfScriptDelete, 19 | Importer: &schema.ResourceImporter{ 20 | StateContext: importJamfScriptState, 21 | }, 22 | Schema: map[string]*schema.Schema{ 23 | "name": { 24 | Type: schema.TypeString, 25 | Required: true, 26 | }, 27 | "id": { 28 | Type: schema.TypeString, 29 | Computed: true, 30 | }, 31 | "category_id": { 32 | Type: schema.TypeString, 33 | Optional: true, 34 | Default: "-1", 35 | }, 36 | "category_name": { 37 | Type: schema.TypeString, 38 | Computed: true, 39 | }, 40 | "priority": { 41 | Type: schema.TypeString, 42 | Optional: true, 43 | Default: "AFTER", 44 | }, 45 | "info": { 46 | Type: schema.TypeString, 47 | Optional: true, 48 | }, 49 | "notes": { 50 | Type: schema.TypeString, 51 | Optional: true, 52 | }, 53 | "parameter4": { 54 | Type: schema.TypeString, 55 | Optional: true, 56 | }, 57 | "parameter5": { 58 | Type: schema.TypeString, 59 | Optional: true, 60 | }, 61 | "parameter6": { 62 | Type: schema.TypeString, 63 | Optional: true, 64 | }, 65 | "parameter7": { 66 | Type: schema.TypeString, 67 | Optional: true, 68 | }, 69 | "parameter8": { 70 | Type: schema.TypeString, 71 | Optional: true, 72 | }, 73 | "parameter9": { 74 | Type: schema.TypeString, 75 | Optional: true, 76 | }, 77 | "parameter10": { 78 | Type: schema.TypeString, 79 | Optional: true, 80 | }, 81 | "parameter11": { 82 | Type: schema.TypeString, 83 | Optional: true, 84 | }, 85 | "os_requirements": { 86 | Type: schema.TypeString, 87 | Optional: true, 88 | }, 89 | "script_contents": { 90 | Type: schema.TypeString, 91 | Optional: true, 92 | ConflictsWith: []string{"file_path"}, 93 | }, 94 | "file_path": { 95 | Type: schema.TypeString, 96 | Optional: true, 97 | ConflictsWith: []string{"script_contents"}, 98 | }, 99 | }, 100 | } 101 | } 102 | 103 | func buildJamfScriptStruct(d *schema.ResourceData) (*jamf.Script, error) { 104 | var out jamf.Script 105 | out.ID = d.Id() 106 | out.Name = d.Get("name").(string) 107 | if v, ok := d.GetOk("category_id"); ok { 108 | out.CategoryID = v.(string) 109 | } 110 | if v, ok := d.GetOk("category_name"); ok { 111 | out.CategoryName = v.(string) 112 | } 113 | if v, ok := d.GetOk("priority"); ok { 114 | out.Priority = v.(string) 115 | } 116 | if v, ok := d.GetOk("info"); ok { 117 | out.Info = v.(string) 118 | } 119 | if v, ok := d.GetOk("notes"); ok { 120 | out.Notes = v.(string) 121 | } 122 | if v, ok := d.GetOk("parameter4"); ok { 123 | out.Parameter4 = v.(string) 124 | } 125 | if v, ok := d.GetOk("parameter5"); ok { 126 | out.Parameter5 = v.(string) 127 | } 128 | if v, ok := d.GetOk("parameter6"); ok { 129 | out.Parameter6 = v.(string) 130 | } 131 | if v, ok := d.GetOk("parameter7"); ok { 132 | out.Parameter7 = v.(string) 133 | } 134 | if v, ok := d.GetOk("parameter8"); ok { 135 | out.Parameter8 = v.(string) 136 | } 137 | if v, ok := d.GetOk("parameter9"); ok { 138 | out.Parameter9 = v.(string) 139 | } 140 | if v, ok := d.GetOk("parameter10"); ok { 141 | out.Parameter10 = v.(string) 142 | } 143 | if v, ok := d.GetOk("parameter11"); ok { 144 | out.Parameter11 = v.(string) 145 | } 146 | if v, ok := d.GetOk("os_requirements"); ok { 147 | out.OsRequirements = v.(string) 148 | } 149 | 150 | filePath, hasFilePath := d.GetOk("file_path") 151 | scriptContents, hasScriptContents := d.GetOk("script_contents") 152 | 153 | if hasFilePath && !hasScriptContents { 154 | content, err := loadFileContent(filePath.(string)) 155 | if err != nil { 156 | return &out, err 157 | } 158 | out.ScriptContents = content 159 | } else if !hasFilePath && hasScriptContents { 160 | out.ScriptContents = scriptContents.(string) 161 | } 162 | 163 | return &out, nil 164 | } 165 | 166 | func resourceJamfScriptCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 167 | c := m.(*jamf.Client) 168 | 169 | b, err := buildJamfScriptStruct(d) 170 | if err != nil { 171 | return diag.FromErr(err) 172 | } 173 | 174 | id, err := c.CreateScript(b) 175 | if err != nil { 176 | return diag.FromErr(err) 177 | } 178 | 179 | d.SetId(id) 180 | 181 | return resourceJamfScriptRead(ctx, d, m) 182 | } 183 | 184 | func resourceJamfScriptRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 185 | var diags diag.Diagnostics 186 | c := m.(*jamf.Client) 187 | 188 | resp, err := c.GetScript(d.Id()) 189 | 190 | if err != nil { 191 | if jamfErr, ok := err.(jamf.Error); ok && jamfErr.StatusCode() == 404 { 192 | d.SetId("") 193 | } else { 194 | return diag.FromErr(err) 195 | } 196 | } else { 197 | deconstructJamfScriptStruct(d, resp) 198 | } 199 | 200 | return diags 201 | } 202 | 203 | func resourceJamfScriptUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 204 | c := m.(*jamf.Client) 205 | 206 | b, err := buildJamfScriptStruct(d) 207 | if err != nil { 208 | return diag.FromErr(err) 209 | } 210 | if _, err := c.UpdateScript(b); err != nil { 211 | return diag.FromErr(err) 212 | } 213 | 214 | return resourceJamfScriptRead(ctx, d, m) 215 | } 216 | 217 | func resourceJamfScriptDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 218 | var diags diag.Diagnostics 219 | c := m.(*jamf.Client) 220 | b, err := buildJamfScriptStruct(d) 221 | if err != nil { 222 | return diag.FromErr(err) 223 | } 224 | 225 | if _, err := c.DeleteScript(b.ID); err != nil { 226 | return diag.FromErr(err) 227 | } 228 | 229 | d.SetId("") 230 | 231 | return diags 232 | } 233 | 234 | func importJamfScriptState(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { 235 | c := m.(*jamf.Client) 236 | d.SetId(d.Id()) 237 | resp, err := c.GetScript(d.Id()) 238 | if err != nil { 239 | return nil, err 240 | } 241 | 242 | deconstructJamfScriptStruct(d, resp) 243 | 244 | return []*schema.ResourceData{d}, nil 245 | } 246 | 247 | // loadFileContent returns contents of a file in a given path 248 | func loadFileContent(v string) (string, error) { 249 | filename, err := homedir.Expand(v) 250 | if err != nil { 251 | return "", err 252 | } 253 | fileContent, err := ioutil.ReadFile(filename) 254 | if err != nil { 255 | return "", err 256 | } 257 | 258 | return string(fileContent), err 259 | } 260 | -------------------------------------------------------------------------------- /jamf/resource_jamf_smartComputerGroup.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strconv" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | "github.com/sioncojp/go-jamf-api" 11 | ) 12 | 13 | func resourceJamfSmartComputerGroup() *schema.Resource { 14 | return &schema.Resource{ 15 | CreateContext: resourceJamfSmartComputerGroupCreate, 16 | ReadContext: resourceJamfSmartComputerGroupRead, 17 | UpdateContext: resourceJamfSmartComputerGroupUpdate, 18 | DeleteContext: resourceJamfSmartComputerGroupDelete, 19 | Importer: &schema.ResourceImporter{ 20 | StateContext: importJamfSmartComputerGroupState, 21 | }, 22 | Schema: map[string]*schema.Schema{ 23 | "name": { 24 | Type: schema.TypeString, 25 | Required: true, 26 | }, 27 | "id": { 28 | Type: schema.TypeString, 29 | Computed: true, 30 | }, 31 | "site": { 32 | Type: schema.TypeSet, 33 | Optional: true, 34 | MaxItems: 1, 35 | Elem: &schema.Resource{ 36 | Schema: map[string]*schema.Schema{ 37 | "id": { 38 | Type: schema.TypeInt, 39 | Computed: true, 40 | }, 41 | "name": { 42 | Type: schema.TypeString, 43 | Required: true, 44 | }, 45 | }, 46 | }, 47 | }, 48 | "criteria": { 49 | Type: schema.TypeSet, 50 | Optional: true, 51 | Elem: &schema.Resource{ 52 | Schema: map[string]*schema.Schema{ 53 | "priority": { 54 | Type: schema.TypeInt, 55 | Required: true, 56 | }, 57 | "name": { 58 | Type: schema.TypeString, 59 | Required: true, 60 | }, 61 | "and_or": { 62 | Type: schema.TypeString, 63 | Optional: true, 64 | Default: "and", 65 | }, 66 | "search_type": { 67 | Type: schema.TypeString, 68 | Required: true, 69 | }, 70 | "search_value": { 71 | Type: schema.TypeString, 72 | Required: true, 73 | }, 74 | "opening_paren": { 75 | Type: schema.TypeBool, 76 | Optional: true, 77 | Default: false, 78 | }, 79 | "closing_paren": { 80 | Type: schema.TypeBool, 81 | Optional: true, 82 | Default: false, 83 | }, 84 | }, 85 | }, 86 | }, 87 | "computer": { 88 | Type: schema.TypeSet, 89 | Computed: true, 90 | Elem: &schema.Resource{ 91 | Schema: map[string]*schema.Schema{ 92 | "id": { 93 | Type: schema.TypeInt, 94 | Computed: true, 95 | }, 96 | "name": { 97 | Type: schema.TypeString, 98 | Computed: true, 99 | }, 100 | "serial_number": { 101 | Type: schema.TypeString, 102 | Computed: true, 103 | }, 104 | }, 105 | }, 106 | }, 107 | }, 108 | } 109 | } 110 | 111 | func buildJamfSmartComputerGroupStruct(d *schema.ResourceData) *jamf.ComputerGroup { 112 | var out jamf.ComputerGroup 113 | id, _ := strconv.Atoi(d.Id()) 114 | out.ID = id 115 | out.Name = d.Get("name").(string) 116 | out.IsSmart = true 117 | if v, ok := d.GetOk("site"); ok { 118 | siteList := v.(*schema.Set).List() 119 | site := siteList[0].(map[string]interface{}) 120 | if val, ok := site["name"].(string); ok { 121 | out.Site.Name = val 122 | } 123 | if val, ok := site["id"].(int); ok { 124 | out.Site.ID = val 125 | } 126 | } 127 | 128 | if v, ok := d.GetOk("criteria"); ok { 129 | criteria := v.(*schema.Set).List() 130 | criteriaList := []jamf.ComputerGroupCriterion{} 131 | for _, c := range criteria { 132 | criteriaData := c.(map[string]interface{}) 133 | criterion := jamf.ComputerGroupCriterion{} 134 | criterion.Priority = criteriaData["priority"].(int) 135 | criterion.Name = criteriaData["name"].(string) 136 | andOr := criteriaData["and_or"].(string) 137 | if andOr == "and" { 138 | criterion.AndOr = jamf.And 139 | } else { 140 | criterion.AndOr = jamf.Or 141 | } 142 | criterion.SearchType = criteriaData["search_type"].(string) 143 | criterion.SearchValue = criteriaData["search_value"].(string) 144 | 145 | if val, ok := criteriaData["opening_paren"].(bool); ok { 146 | criterion.OpeningParen = val 147 | } 148 | if val, ok := criteriaData["closing_paren"].(bool); ok { 149 | criterion.ClosingParen = val 150 | } 151 | criteriaList = append(criteriaList, criterion) 152 | } 153 | out.Criteria = criteriaList 154 | } 155 | 156 | return &out 157 | } 158 | 159 | func resourceJamfSmartComputerGroupCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 160 | c := m.(*jamf.Client) 161 | 162 | b := buildJamfSmartComputerGroupStruct(d) 163 | 164 | group := &jamf.ComputerGroupRequest{} 165 | group.Name = b.Name 166 | if b.Site.Name != "" { 167 | group.Site = b.Site 168 | } 169 | group.IsSmart = b.IsSmart 170 | group.Criteria = b.Criteria 171 | 172 | id, err := c.CreateComputerGroup(group) 173 | if err != nil { 174 | return diag.FromErr(err) 175 | } 176 | 177 | d.SetId(strconv.Itoa(id)) 178 | 179 | return resourceJamfSmartComputerGroupRead(ctx, d, m) 180 | } 181 | 182 | func resourceJamfSmartComputerGroupRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 183 | var diags diag.Diagnostics 184 | c := m.(*jamf.Client) 185 | 186 | id, err := strconv.Atoi(d.Id()) 187 | if err != nil { 188 | return diag.FromErr(err) 189 | } 190 | resp, err := c.GetComputerGroup(id) 191 | if err != nil { 192 | if jamfErr, ok := err.(jamf.Error); ok && jamfErr.StatusCode() == 404 { 193 | d.SetId("") 194 | } else { 195 | return diag.FromErr(err) 196 | } 197 | } else { 198 | deconstructJamfComputerGroupStruct(d, resp) 199 | } 200 | 201 | return diags 202 | } 203 | 204 | func resourceJamfSmartComputerGroupUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 205 | c := m.(*jamf.Client) 206 | 207 | b := buildJamfSmartComputerGroupStruct(d) 208 | 209 | if _, err := c.UpdateComputerGroup(b); err != nil { 210 | return diag.FromErr(err) 211 | } 212 | 213 | return resourceJamfSmartComputerGroupRead(ctx, d, m) 214 | } 215 | 216 | func resourceJamfSmartComputerGroupDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 217 | var diags diag.Diagnostics 218 | c := m.(*jamf.Client) 219 | b := buildJamfSmartComputerGroupStruct(d) 220 | 221 | if _, err := c.DeleteComputerGroup(b.ID); err != nil { 222 | return diag.FromErr(err) 223 | } 224 | 225 | d.SetId("") 226 | 227 | return diags 228 | } 229 | 230 | func importJamfSmartComputerGroupState(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { 231 | c := m.(*jamf.Client) 232 | d.SetId(d.Id()) 233 | id, err := strconv.Atoi(d.Id()) 234 | if err != nil { 235 | return nil, err 236 | } 237 | resp, err := c.GetComputerGroup(id) 238 | if err != nil { 239 | return nil, fmt.Errorf("cannot get Computer Group data") 240 | } 241 | 242 | deconstructJamfComputerGroupStruct(d, resp) 243 | 244 | return []*schema.ResourceData{d}, nil 245 | } 246 | -------------------------------------------------------------------------------- /website/docs/d/policy.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "jamf" 3 | subcategory: "Data Sources" 4 | page_title: "Jamf: jamf_policy" 5 | description: |- 6 | Provides details about a policy. 7 | --- 8 | 9 | # Data Source: jamf_policy 10 | 11 | Use this data source to get the policy information. 12 | 13 | The policy data source allows access to details of a specific 14 | policy within Jamf. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | data "jamf_policy" "test_policy_1" { 20 | name = "Test Policy 1" 21 | } 22 | ``` 23 | 24 | ## Argument Reference 25 | 26 | The following arguments are supported: 27 | 28 | * `name` - (Required) The name of the policy 29 | 30 | ## Attributes Reference 31 | 32 | In addition to the above arguments, the following attributes are exported: 33 | 34 | * `id` - ID of the policy 35 | * `general` - General information of the Policy 36 | * `id` - ID of the policy 37 | * `name` - name of the policy 38 | * `enabled` - Enabled state of the policy 39 | * `trigger` - Trigger of the policy 40 | * `trigger_checkin` - Check-in trigger state 41 | * `trigger_enrollment_complete`- Enrollment Complete trigger state 42 | * `trigger_login` - Login trigger state 43 | * `trigger_logout` - Logout trigger state 44 | * `trigger_network_state_changed` - Network state change trigger state 45 | * `trigger_startup` - Startup trigger state 46 | * `trigger_other` - Custom trigger event 47 | * `frequency` - Frequency of the policy 48 | * `retry_event` - Retry event of the policy 49 | * `retry_attempts` - Number of retry events for the policy 50 | * `notify_on_each_failed_retry` - Notify state of the retry event for the policy 51 | * `location_user_only` - ? 52 | * `target_drive` - Target drive of the policy 53 | * `offline` - State of the policy being available offline 54 | * `network_requirements` - Network requirements of the policy 55 | * `category` - Category of the policy 56 | * `id` - ID of the category 57 | * `name` - Name of the category 58 | * `date_time_limitations` - Date and Time limitations of the policy 59 | * `activation_date` - Activation date written out 60 | * `activation_date_epoch` - Activation date in epoch 61 | * `activation_date_utc` - Activation date written out in UTC 62 | * `expiration_date` - Expiration date written out 63 | * `expiration_date_epoch` - Expiration date in epoch 64 | * `expiration_date_utc` - Expiration date written out in UTC 65 | * `no_execute_on` - ? 66 | * `no_execute_start` - ? 67 | * `no_execute_end` - ? 68 | * `network_limitations` - Network Limitations of the policy 69 | * `minimum_network_connection` - Minimum network connections required 70 | * `any_ip_address` - IP Address range required 71 | * `network_segments` - Network Segments required 72 | * `override_default_settings` - Default settings of the policy to override 73 | * `target_drive` - Default target installation drive 74 | * `distribution_point` - Default distribution point 75 | * `force_afp_smb` - ? 76 | * `sus` - ? 77 | * `netboot_server` - ? 78 | * `site` - Site of the policy 79 | * `id` - ID of the site 80 | * `name` - Name of the site 81 | * `scope` - Scope of the policy 82 | * `all_computers` - State of the policy being scoped to all computers 83 | * `computer` - Details a computer the policy is scoped to 84 | * `id` - ID of the computer 85 | * `name` - Name of the computer 86 | * `udid` - UDID of the computer 87 | * `computer_group` - Details a computer group the policy is scoped to 88 | * `id` - ID of the computer group 89 | * `name`- Name of the computer group 90 | * `building` - Details a building the policy is scoped to 91 | * `id` - ID of the building 92 | * `name` - Name of the building 93 | * `department` - Details a department the policy is scoped to 94 | * `id` - ID of the department 95 | * `name` - Name of the department 96 | * `self_service` - Self Service configuration of the policy 97 | * `use_for_self_service` - Self Service enablement state 98 | * `self_service_display_name` - Display name 99 | * `install_button_text` - Install Button Text 100 | * `reinstall_button_text`- Reinstall Button Text 101 | * `self_service_description` - Description 102 | * `force_users_to_view_description` - State of description viewing enforcement 103 | * `feature_on_main_page` - State of main page featuring 104 | * `self_service_icon` - Self Service icon configuration 105 | * `id` - ID of the icon 106 | * `filename` - Filename of the icon 107 | * `uri` - URI of the icon 108 | * `self_service_category` - Self Service category configuration 109 | * `id` - ID of the category 110 | * `name` - Name of the category 111 | * `display_in` - Display state in the category 112 | * `feature_in` - Feature state in the category 113 | * `package` - Package information assigned to the policy 114 | * `id` - ID of the package 115 | * `name` - Name of the package 116 | * `action` - Action of the package 117 | * `fut` - Fill user template state of the DMG 118 | * `feu` - Fill existing users state of the DMG 119 | * `update_autorun` - Update autorun state of the DMG 120 | * `script `- Script information assigned to the policy 121 | * `id` - ID of the script 122 | * `name` - Name of the script 123 | * `priority` - Priority of the script 124 | * `parameter4` - Parameter 4 of the script 125 | * `parameter5` - Parameter 5 of the script 126 | * `parameter6` - Parameter 6 of the script 127 | * `parameter7` - Parameter 7 of the script 128 | * `parameter8` - Parameter 8 of the script 129 | * `parameter9` - Parameter 9 of the script 130 | * `parameter10` - Parameter 10 of the script 131 | * `parameter11` - Parameter 11 of the script 132 | * `reboot` - Reboot information assigned to the policy 133 | * `message` - User message 134 | * `startup_disk` - Startup disk after reboot 135 | * `specify_startup` - ? 136 | * `no_user_logged_in` - No user logged in functionality of the reboot 137 | * `user_logged_in` - User logged in functionality of the reboot 138 | * `minutes_until_reboot` - Minutes until the reboot triggers after reboot 139 | * `start_reboot_timer_immediately` - Reboot timer immediate state 140 | * `file_vault_2_reboot` - ? 141 | * `maintenance` - Maintenance information assigned to the policy 142 | * `recon` - Recon state after policy completes 143 | * `reset_name` - Reset name state 144 | * `install_all_cached_packages` - Cached package install state 145 | * `heal` - ? 146 | * `prebindings` - ? 147 | * `permissions` - Disk permissions repair state 148 | * `byhost` - ByHost repair state 149 | * `system_cache` - Flush system cache state 150 | * `user_cache` - Flush user cache state 151 | * `verify` - Verify startup disk state 152 | * `files_and_processes` - File and Process information assigned to the policy 153 | * `search_by_path` - Path to search for file 154 | * `delete_file` - Deletion state of a file found by `search_by_path` 155 | * `locate_file` - Path to search for file 156 | * `update_locate_database` - Local database update state of a file found by `locate_file` 157 | * `spotlight_search` - Spotlight search for file 158 | * `search_for_process` - Process to search for 159 | * `kill_process` - Kill state for process found by `search_for_process` 160 | * `run_command` - Command to execute as `root` 161 | * `user_interaction` - User Interaction information assigned to the policy 162 | * `message_start` - Message at start of policy 163 | * `message_finish` - Message at end of policy 164 | * `allow_users_to_defer` - Deferral state 165 | * `allow_deferral_until_utc` - Deferral until date in UTC 166 | * `allow_deferral_minutes` - Minutes to defer 167 | -------------------------------------------------------------------------------- /website/docs/r/policy.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "jamf" 3 | subcategory: "Resources" 4 | page_title: "Jamf: jamf_policy" 5 | description: |- 6 | Provides details about a policy. 7 | --- 8 | 9 | # Data Source: jamf_policy 10 | 11 | Provides a policy. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | resource "jamf_policy" "test_policy_1" { 17 | general { 18 | name = "Test Policy 1" 19 | category { 20 | id = "-1" 21 | name = "No category assigned" 22 | } 23 | network_limitations {} 24 | override_default_settings {} 25 | site {} 26 | } 27 | self_service { 28 | self_service_icon {} 29 | } 30 | scope {} 31 | reboot {} 32 | } 33 | ``` 34 | 35 | ## Argument Reference 36 | 37 | The following arguments are supported: 38 | 39 | * `general` - (Required) General information of the Policy 40 | * `name` - (Required) The name of the policy 41 | * `enabled` - (Optional) Enabled state of the policy 42 | * `trigger` - (Optional) Trigger of the policy, Default `EVENT` 43 | * `trigger_checkin` - (Optional) Check-in trigger state 44 | * `trigger_enrollment_complete` - (Optional) Enrollment Complete trigger state 45 | * `trigger_login` - (Optional) Login trigger state 46 | * `trigger_logout` - (Optional) Logout trigger state 47 | * `trigger_network_state_changed` - (Optional) Network state change trigger state 48 | * `trigger_startup` - (Optional) Startup trigger state 49 | * `trigger_other` - (Optional) Custom trigger event 50 | * `frequency` - (Optional) Frequency of the policy, Default `Once per computer` 51 | * `retry_event` - (Optional) Retry event of the policy, Default `none` 52 | * `retry_attempts` - (Optional) Number of retry events for the policy, Default `-1` 53 | * `notify_on_each_failed_retry` - (Optional) Notify state of the retry event for the policy 54 | * `location_user_only` - (Optional) ? 55 | * `target_drive` - (Optional) Target drive of the policy, Default `/` 56 | * `offline` - (Optional) State of the policy being available offline 57 | * `network_requirements` - (Optional) Network requirements of the policy, Default `Any` 58 | * `category` - (Required) Category of the policy 59 | * `id` - (Required) ID of the category, for default set to `-1` 60 | * `name` - (Required) Name of the category, for default set to `No category assigned` 61 | * `date_time_limitations` - (Optional) Date and Time limitations of the policy 62 | * `activation_date_epoch` - (Optional) Activation date in epoch, Default `0` 63 | * `expiration_date_epoch` - (Optional) Expiration date in epoch, Default `0` 64 | * `no_execute_on` - (Optional) ? 65 | * `no_execute_start` - (Optional) ? 66 | * `no_execute_end` - (Optional) ? 67 | * `network_limitations` - (Required) Network Limitations of the policy 68 | * `minimum_network_connection` - (Optional) Minimum network connections required, Default `No Minimum` 69 | * `any_ip_address` - (Optional) IP Address range required, Default `true` 70 | * `network_segments` - (Optional) Network Segments required 71 | * `override_default_settings` - (Required) Default settings of the policy to override 72 | * `target_drive` - (Optional) Default target installation drive, Default `default` 73 | * `distribution_point` - (Optional) Default distribution point 74 | * `force_afp_smb` - (Optional) ? 75 | * `sus` - (Optional) ?, Default `default` 76 | * `netboot_server` - (Optional) ?, Default `current` 77 | * `site` - (Required) Site of the policy 78 | * `id` - (Optional) ID of the site, Default `-1` 79 | * `scope` - (Required) Scope of the policy 80 | * `all_computers` - (Optional) State of the policy being scoped to all computers 81 | * `computer` - (Optional) Details a computer the policy is scoped to 82 | * `id` - (Required) ID of the computer 83 | * `computer_group` - (Optional) Details a computer group the policy is scoped to 84 | * `id` - (Required) ID of the computer group 85 | * `building` - (Optional) Details a building the policy is scoped to 86 | * `id` - (Required) ID of the building 87 | * `department` - (Optional) Details a department the policy is scoped to 88 | * `id` - (Required) ID of the department 89 | * `self_service` - (Required) Self Service configuration of the policy 90 | * `use_for_self_service` - (Optional) Self Service enablement state 91 | * `self_service_display_name` - (Optional) Display name 92 | * `install_button_text` - (Optional) Install Button Text, Default `Install` 93 | * `reinstall_button_text` - (Optional) Reinstall Button Text, Default `Reinstall` 94 | * `self_service_description` - (Optional) Description 95 | * `force_users_to_view_description` - (Optional) State of description viewing enforcement 96 | * `feature_on_main_page` - (Optional) State of main page featuring 97 | * `self_service_icon` - (Required) Self Service icon configuration 98 | * `id` - (Optional) ID of the icon, Default `0` 99 | * `self_service_category` - (Optional) Self Service category configuration 100 | * `id` - (Optional) ID of the category, if policy category is defined this will be need to be set to same category ID 101 | * `display_in` - (Optional) Display state in the category 102 | * `feature_in` - (Optional) Feature state in the category 103 | * `package` - (Optional) Package information assigned to the policy 104 | * `id` - (Required) ID of the package 105 | * `action` - (Optional) Action of the package, Default `INSTALL` 106 | * `fut` - (Optional) Fill user template state of the DMG 107 | * `feu` - (Optional) Fill existing users state of the DMG 108 | * `update_autorun` - (Optional) Update autorun state of the DMG 109 | * `script ` - (Optional) Script information assigned to the policy 110 | * `id` - (Required) ID of the script 111 | * `name` - (Optional) Name of the script 112 | * `priority` - (Optional) Priority of the script, Default `AFTER` 113 | * `parameter4` - (Optional) Parameter 4 of the script 114 | * `parameter5` - (Optional) Parameter 5 of the script 115 | * `parameter6` - (Optional) Parameter 6 of the script 116 | * `parameter7` - (Optional) Parameter 7 of the script 117 | * `parameter8` - (Optional) Parameter 8 of the script 118 | * `parameter9` - (Optional) Parameter 9 of the script 119 | * `parameter10` - (Optional) Parameter 10 of the script 120 | * `parameter11` - (Optional) Parameter 11 of the script 121 | * `reboot` - (Required) Reboot information assigned to the policy 122 | * `message` - (Optional) User message 123 | * `startup_disk` - (Optional) Startup disk after reboot, Default `Current Startup Disk` 124 | * `specify_startup` - (Optional) ? 125 | * `no_user_logged_in` - (Optional) No user logged in functionality of the reboot, Default `Do not restart` 126 | * `user_logged_in` - (Optional) User logged in functionality of the reboot, Default `Do not restart` 127 | * `minutes_until_reboot` - (Optional) Minutes until the reboot triggers after reboot, Default `5` 128 | * `start_reboot_timer_immediately`- (Optional) Reboot timer immediate state 129 | * `file_vault_2_reboot` - (Optional) ? 130 | * `maintenance` - (Optional) Maintenance information assigned to the policy 131 | * `recon` - (Optional) Recon state after policy completes 132 | * `reset_name` - (Optional) Reset name state 133 | * `install_all_cached_packages` - (Optional) Cached package install state 134 | * `heal` - (Optional) ? 135 | * `prebindings` - (Optional) ? 136 | * `permissions` - (Optional) Disk permissions repair state 137 | * `byhost` - (Optional) ByHost repair state 138 | * `system_cache` - (Optional) Flush system cache state 139 | * `user_cache` - (Optional) Flush user cache state 140 | * `verify` - (Optional) Verify startup disk state 141 | * `files_and_processes` - (Optional) File and Process information assigned to the policy 142 | * `search_by_path` - (Optional) Path to search for file 143 | * `delete_file` - (Optional) Deletion state of a file found by `search_by_path` 144 | * `locate_file` - (Optional) Path to search for file 145 | * `update_locate_database` - (Optional) Local database update state of a file found by `locate_file` 146 | * `spotlight_search` - (Optional) Spotlight search for file 147 | * `search_for_process` - (Optional) Process to search for 148 | * `kill_process` - (Optional) Kill state for process found by `search_for_process` 149 | * `run_command` - (Optional) Command to execute as `root` 150 | * `user_interaction` - (Optional) User Interaction information assigned to the policy 151 | * `message_start` - (Optional) Message at start of policy 152 | * `message_finish` - (Optional) Message at end of policy 153 | * `allow_users_to_defer` - (Optional) Deferral state 154 | * `allow_deferral_until_utc` - (Optional) Deferral until date in UTC 155 | * `allow_deferral_minutes` - (Optional) Minutes to defer 156 | 157 | ## Attributes Reference 158 | 159 | In addition to the above arguments, the following attributes are exported: 160 | 161 | * `id` - ID of the policy 162 | * `general` - General information of the Policy 163 | * `id` - ID of the policy 164 | * `date_time_limitations` - Date and Time limitations of the policy 165 | * `activation_date` - Activation date written out 166 | * `activation_date_utc` - Activation date written out in UTC 167 | * `expiration_date` - Expiration date written out 168 | * `expiration_date_utc` - Expiration date written out in UTC 169 | * `site` - Site of the policy 170 | * `name` - Name of the site 171 | * `scope` - Scope of the policy 172 | * `computer` - Details a computer the policy is scoped to 173 | * `name` - Name of the computer 174 | * `udid` - UDID of the computer 175 | * `computer_group` - Details a computer group the policy is scoped to 176 | * `name`- Name of the computer group 177 | * `building` - Details a building the policy is scoped to 178 | * `name` - Name of the building 179 | * `department` - Details a department the policy is scoped to 180 | * `name` - Name of the department 181 | * `self_service` - Self Service configuration of the policy 182 | * `self_service_icon` - Self Service icon configuration 183 | * `filename` - Filename of the icon 184 | * `uri` - URI of the icon 185 | * `self_service_category` - Self Service category configuration 186 | * `name` - Name of the category 187 | * `package` - Package information assigned to the policy 188 | * `name` - Name of the package 189 | * `script `- Script information assigned to the policy 190 | * `name` - Name of the script 191 | -------------------------------------------------------------------------------- /jamf/data_source_jamf_policy.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 9 | "github.com/sioncojp/go-jamf-api" 10 | ) 11 | 12 | func dataSourceJamfPolicy() *schema.Resource { 13 | return &schema.Resource{ 14 | ReadContext: dataSourceJamfPolicyRead, 15 | Schema: map[string]*schema.Schema{ 16 | "name": { 17 | Type: schema.TypeString, 18 | Required: true, 19 | }, 20 | "general": { 21 | Type: schema.TypeSet, 22 | Computed: true, 23 | Elem: &schema.Resource{ 24 | Schema: map[string]*schema.Schema{ 25 | "id": { 26 | Type: schema.TypeInt, 27 | Computed: true, 28 | }, 29 | "name": { 30 | Type: schema.TypeString, 31 | Computed: true, 32 | }, 33 | "enabled": { 34 | Type: schema.TypeBool, 35 | Computed: true, 36 | }, 37 | "trigger": { 38 | Type: schema.TypeString, 39 | Computed: true, 40 | }, 41 | "trigger_checkin": { 42 | Type: schema.TypeBool, 43 | Computed: true, 44 | }, 45 | "trigger_enrollment_complete": { 46 | Type: schema.TypeBool, 47 | Computed: true, 48 | }, 49 | "trigger_login": { 50 | Type: schema.TypeBool, 51 | Computed: true, 52 | }, 53 | "trigger_logout": { 54 | Type: schema.TypeBool, 55 | Computed: true, 56 | }, 57 | "trigger_network_state_changed": { 58 | Type: schema.TypeBool, 59 | Computed: true, 60 | }, 61 | "trigger_startup": { 62 | Type: schema.TypeBool, 63 | Computed: true, 64 | }, 65 | "trigger_other": { 66 | Type: schema.TypeString, 67 | Computed: true, 68 | }, 69 | "frequency": { 70 | Type: schema.TypeString, 71 | Computed: true, 72 | }, 73 | "retry_event": { 74 | Type: schema.TypeString, 75 | Computed: true, 76 | }, 77 | "retry_attempts": { 78 | Type: schema.TypeInt, 79 | Computed: true, 80 | }, 81 | "notify_on_each_failed_retry": { 82 | Type: schema.TypeBool, 83 | Computed: true, 84 | }, 85 | "location_user_only": { 86 | Type: schema.TypeBool, 87 | Computed: true, 88 | }, 89 | "target_drive": { 90 | Type: schema.TypeString, 91 | Computed: true, 92 | }, 93 | "offline": { 94 | Type: schema.TypeBool, 95 | Computed: true, 96 | }, 97 | "network_requirements": { 98 | Type: schema.TypeString, 99 | Computed: true, 100 | }, 101 | "category": { 102 | Type: schema.TypeSet, 103 | Computed: true, 104 | Elem: &schema.Resource{ 105 | Schema: map[string]*schema.Schema{ 106 | "id": { 107 | Type: schema.TypeString, 108 | Computed: true, 109 | }, 110 | "name": { 111 | Type: schema.TypeString, 112 | Computed: true, 113 | }, 114 | }, 115 | }, 116 | }, 117 | "date_time_limitations": { 118 | Type: schema.TypeSet, 119 | Computed: true, 120 | Elem: &schema.Resource{ 121 | Schema: map[string]*schema.Schema{ 122 | "activation_date": { 123 | Type: schema.TypeString, 124 | Computed: true, 125 | }, 126 | "activation_date_epoch": { 127 | Type: schema.TypeInt, 128 | Computed: true, 129 | }, 130 | "activation_date_utc": { 131 | Type: schema.TypeString, 132 | Computed: true, 133 | }, 134 | "expiration_date": { 135 | Type: schema.TypeString, 136 | Computed: true, 137 | }, 138 | "expiration_date_epoch": { 139 | Type: schema.TypeInt, 140 | Computed: true, 141 | }, 142 | "expiration_date_utc": { 143 | Type: schema.TypeString, 144 | Computed: true, 145 | }, 146 | "no_execute_on": { 147 | Type: schema.TypeString, 148 | Computed: true, 149 | }, 150 | "no_execute_start": { 151 | Type: schema.TypeString, 152 | Computed: true, 153 | }, 154 | "no_execute_end": { 155 | Type: schema.TypeString, 156 | Computed: true, 157 | }, 158 | }, 159 | }, 160 | }, 161 | "network_limitations": { 162 | Type: schema.TypeSet, 163 | Computed: true, 164 | Elem: &schema.Resource{ 165 | Schema: map[string]*schema.Schema{ 166 | "minimum_network_connection": { 167 | Type: schema.TypeString, 168 | Computed: true, 169 | }, 170 | "any_ip_address": { 171 | Type: schema.TypeBool, 172 | Computed: true, 173 | }, 174 | "network_segments": { 175 | Type: schema.TypeString, 176 | Computed: true, 177 | }, 178 | }, 179 | }, 180 | }, 181 | "override_default_settings": { 182 | Type: schema.TypeSet, 183 | Computed: true, 184 | Elem: &schema.Resource{ 185 | Schema: map[string]*schema.Schema{ 186 | "target_drive": { 187 | Type: schema.TypeString, 188 | Computed: true, 189 | }, 190 | "distribution_point": { 191 | Type: schema.TypeString, 192 | Computed: true, 193 | }, 194 | "force_afp_smb": { 195 | Type: schema.TypeBool, 196 | Computed: true, 197 | }, 198 | "sus": { 199 | Type: schema.TypeString, 200 | Computed: true, 201 | }, 202 | "netboot_server": { 203 | Type: schema.TypeString, 204 | Computed: true, 205 | }, 206 | }, 207 | }, 208 | }, 209 | "site": { 210 | Type: schema.TypeSet, 211 | Computed: true, 212 | Elem: &schema.Resource{ 213 | Schema: map[string]*schema.Schema{ 214 | "id": { 215 | Type: schema.TypeInt, 216 | Computed: true, 217 | }, 218 | "name": { 219 | Type: schema.TypeString, 220 | Computed: true, 221 | }, 222 | }, 223 | }, 224 | }, 225 | }, 226 | }, 227 | }, 228 | "scope": { 229 | Type: schema.TypeSet, 230 | Computed: true, 231 | Elem: &schema.Resource{ 232 | Schema: map[string]*schema.Schema{ 233 | "all_computers": { 234 | Type: schema.TypeBool, 235 | Computed: true, 236 | }, 237 | "computers": { 238 | Type: schema.TypeSet, 239 | Computed: true, 240 | Elem: &schema.Resource{ 241 | Schema: map[string]*schema.Schema{ 242 | "id": { 243 | Type: schema.TypeInt, 244 | Computed: true, 245 | }, 246 | "name": { 247 | Type: schema.TypeString, 248 | Computed: true, 249 | }, 250 | "udid": { 251 | Type: schema.TypeString, 252 | Computed: true, 253 | }, 254 | }, 255 | }, 256 | }, 257 | "computer_groups": { 258 | Type: schema.TypeSet, 259 | Computed: true, 260 | Elem: &schema.Resource{ 261 | Schema: map[string]*schema.Schema{ 262 | "id": { 263 | Type: schema.TypeInt, 264 | Computed: true, 265 | }, 266 | "name": { 267 | Type: schema.TypeString, 268 | Computed: true, 269 | }, 270 | }, 271 | }, 272 | }, 273 | "buildings": { 274 | Type: schema.TypeSet, 275 | Computed: true, 276 | Elem: &schema.Resource{ 277 | Schema: map[string]*schema.Schema{ 278 | "id": { 279 | Type: schema.TypeInt, 280 | Computed: true, 281 | }, 282 | "name": { 283 | Type: schema.TypeString, 284 | Computed: true, 285 | }, 286 | }, 287 | }, 288 | }, 289 | "departments": { 290 | Type: schema.TypeSet, 291 | Computed: true, 292 | Elem: &schema.Resource{ 293 | Schema: map[string]*schema.Schema{ 294 | "id": { 295 | Type: schema.TypeInt, 296 | Computed: true, 297 | }, 298 | "name": { 299 | Type: schema.TypeString, 300 | Computed: true, 301 | }, 302 | }, 303 | }, 304 | }, 305 | }, 306 | }, 307 | }, 308 | "self_service": { 309 | Type: schema.TypeSet, 310 | Computed: true, 311 | Elem: &schema.Resource{ 312 | Schema: map[string]*schema.Schema{ 313 | "use_for_self_service": { 314 | Type: schema.TypeBool, 315 | Computed: true, 316 | }, 317 | "self_service_display_name": { 318 | Type: schema.TypeString, 319 | Computed: true, 320 | }, 321 | "install_button_text": { 322 | Type: schema.TypeString, 323 | Computed: true, 324 | }, 325 | "reinstall_button_text": { 326 | Type: schema.TypeString, 327 | Computed: true, 328 | }, 329 | "self_service_description": { 330 | Type: schema.TypeString, 331 | Computed: true, 332 | }, 333 | "force_users_to_view_description": { 334 | Type: schema.TypeBool, 335 | Computed: true, 336 | }, 337 | "feature_on_main_page": { 338 | Type: schema.TypeBool, 339 | Computed: true, 340 | }, 341 | "self_service_icon": { 342 | Type: schema.TypeSet, 343 | Computed: true, 344 | Elem: &schema.Resource{ 345 | Schema: map[string]*schema.Schema{ 346 | "id": { 347 | Type: schema.TypeInt, 348 | Computed: true, 349 | }, 350 | "filename": { 351 | Type: schema.TypeString, 352 | Computed: true, 353 | }, 354 | "uri": { 355 | Type: schema.TypeString, 356 | Computed: true, 357 | }, 358 | }, 359 | }, 360 | }, 361 | "self_service_category": { 362 | Type: schema.TypeSet, 363 | Computed: true, 364 | Elem: &schema.Resource{ 365 | Schema: map[string]*schema.Schema{ 366 | "id": { 367 | Type: schema.TypeInt, 368 | Computed: true, 369 | }, 370 | "name": { 371 | Type: schema.TypeString, 372 | Computed: true, 373 | }, 374 | "display_in": { 375 | Type: schema.TypeBool, 376 | Computed: true, 377 | }, 378 | "feature_in": { 379 | Type: schema.TypeBool, 380 | Computed: true, 381 | }, 382 | }, 383 | }, 384 | }, 385 | }, 386 | }, 387 | }, 388 | "package": { 389 | Type: schema.TypeSet, 390 | Computed: true, 391 | Elem: &schema.Resource{ 392 | Schema: map[string]*schema.Schema{ 393 | "id": { 394 | Type: schema.TypeInt, 395 | Computed: true, 396 | }, 397 | "name": { 398 | Type: schema.TypeString, 399 | Computed: true, 400 | }, 401 | "action": { 402 | Type: schema.TypeString, 403 | Computed: true, 404 | }, 405 | "fut": { 406 | Type: schema.TypeBool, 407 | Computed: true, 408 | }, 409 | "feu": { 410 | Type: schema.TypeBool, 411 | Computed: true, 412 | }, 413 | "update_autorun": { 414 | Type: schema.TypeBool, 415 | Computed: true, 416 | }, 417 | }, 418 | }, 419 | }, 420 | "script": { 421 | Type: schema.TypeSet, 422 | Computed: true, 423 | Elem: &schema.Resource{ 424 | Schema: map[string]*schema.Schema{ 425 | "id": { 426 | Type: schema.TypeString, 427 | Computed: true, 428 | }, 429 | "name": { 430 | Type: schema.TypeString, 431 | Computed: true, 432 | }, 433 | "priority": { 434 | Type: schema.TypeString, 435 | Computed: true, 436 | }, 437 | "parameter4": { 438 | Type: schema.TypeString, 439 | Computed: true, 440 | }, 441 | "parameter5": { 442 | Type: schema.TypeString, 443 | Computed: true, 444 | }, 445 | "parameter6": { 446 | Type: schema.TypeString, 447 | Computed: true, 448 | }, 449 | "parameter7": { 450 | Type: schema.TypeString, 451 | Computed: true, 452 | }, 453 | "parameter8": { 454 | Type: schema.TypeString, 455 | Computed: true, 456 | }, 457 | "parameter9": { 458 | Type: schema.TypeString, 459 | Computed: true, 460 | }, 461 | "parameter10": { 462 | Type: schema.TypeString, 463 | Computed: true, 464 | }, 465 | "parameter11": { 466 | Type: schema.TypeString, 467 | Computed: true, 468 | }, 469 | }, 470 | }, 471 | }, 472 | "reboot": { 473 | Type: schema.TypeSet, 474 | Computed: true, 475 | Elem: &schema.Resource{ 476 | Schema: map[string]*schema.Schema{ 477 | "message": { 478 | Type: schema.TypeString, 479 | Computed: true, 480 | }, 481 | "startup_disk": { 482 | Type: schema.TypeString, 483 | Computed: true, 484 | }, 485 | "specify_startup": { 486 | Type: schema.TypeString, 487 | Computed: true, 488 | }, 489 | "no_user_logged_in": { 490 | Type: schema.TypeString, 491 | Computed: true, 492 | }, 493 | "user_logged_in": { 494 | Type: schema.TypeString, 495 | Computed: true, 496 | }, 497 | "minutes_until_reboot": { 498 | Type: schema.TypeInt, 499 | Computed: true, 500 | }, 501 | "start_reboot_timer_immediately": { 502 | Type: schema.TypeBool, 503 | Computed: true, 504 | }, 505 | "file_vault_2_reboot": { 506 | Type: schema.TypeBool, 507 | Computed: true, 508 | }, 509 | }, 510 | }, 511 | }, 512 | "maintenance": { 513 | Type: schema.TypeSet, 514 | Computed: true, 515 | Elem: &schema.Resource{ 516 | Schema: map[string]*schema.Schema{ 517 | "recon": { 518 | Type: schema.TypeBool, 519 | Computed: true, 520 | }, 521 | "reset_name": { 522 | Type: schema.TypeBool, 523 | Computed: true, 524 | }, 525 | "install_all_cached_packages": { 526 | Type: schema.TypeBool, 527 | Computed: true, 528 | }, 529 | "heal": { 530 | Type: schema.TypeBool, 531 | Computed: true, 532 | }, 533 | "prebindings": { 534 | Type: schema.TypeBool, 535 | Computed: true, 536 | }, 537 | "permissions": { 538 | Type: schema.TypeBool, 539 | Computed: true, 540 | }, 541 | "byhost": { 542 | Type: schema.TypeBool, 543 | Computed: true, 544 | }, 545 | "system_cache": { 546 | Type: schema.TypeBool, 547 | Computed: true, 548 | }, 549 | "user_cache": { 550 | Type: schema.TypeBool, 551 | Computed: true, 552 | }, 553 | "verify": { 554 | Type: schema.TypeBool, 555 | Computed: true, 556 | }, 557 | }, 558 | }, 559 | }, 560 | "files_and_processes": { 561 | Type: schema.TypeSet, 562 | Computed: true, 563 | Elem: &schema.Resource{ 564 | Schema: map[string]*schema.Schema{ 565 | "search_by_path": { 566 | Type: schema.TypeString, 567 | Computed: true, 568 | }, 569 | "delete_file": { 570 | Type: schema.TypeBool, 571 | Computed: true, 572 | }, 573 | "locate_file": { 574 | Type: schema.TypeString, 575 | Computed: true, 576 | }, 577 | "update_locate_database": { 578 | Type: schema.TypeBool, 579 | Computed: true, 580 | }, 581 | "spotlight_search": { 582 | Type: schema.TypeString, 583 | Computed: true, 584 | }, 585 | "search_for_process": { 586 | Type: schema.TypeString, 587 | Computed: true, 588 | }, 589 | "kill_process": { 590 | Type: schema.TypeBool, 591 | Computed: true, 592 | }, 593 | "run_command": { 594 | Type: schema.TypeString, 595 | Computed: true, 596 | }, 597 | }, 598 | }, 599 | }, 600 | "user_interaction": { 601 | Type: schema.TypeSet, 602 | Computed: true, 603 | Elem: &schema.Resource{ 604 | Schema: map[string]*schema.Schema{ 605 | "message_start": { 606 | Type: schema.TypeString, 607 | Computed: true, 608 | }, 609 | "allow_users_to_defer": { 610 | Type: schema.TypeBool, 611 | Computed: true, 612 | }, 613 | "allow_deferral_until_utc": { 614 | Type: schema.TypeString, 615 | Computed: true, 616 | }, 617 | "allow_deferral_minutes": { 618 | Type: schema.TypeInt, 619 | Computed: true, 620 | }, 621 | "message_finish": { 622 | Type: schema.TypeString, 623 | Computed: true, 624 | }, 625 | }, 626 | }, 627 | }, 628 | }, 629 | } 630 | } 631 | 632 | func dataSourceJamfPolicyRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 633 | var diags diag.Diagnostics 634 | c := m.(*jamf.Client) 635 | 636 | resp, err := c.GetPolicyByName(d.Get("name").(string)) 637 | if err != nil { 638 | return diag.FromErr(err) 639 | } 640 | 641 | deconstructJamfPolicyStruct(d, resp) 642 | 643 | return diags 644 | } 645 | 646 | func deconstructJamfPolicyStruct(d *schema.ResourceData, in *jamf.Policy) { 647 | 648 | // General 649 | d.SetId(strconv.Itoa(in.General.ID)) 650 | 651 | general := map[string]interface{}{ 652 | "id": in.General.ID, 653 | "name": in.General.Name, 654 | "enabled": in.General.Enabled, 655 | "trigger": in.General.Trigger, 656 | "trigger_checkin": in.General.TriggerCheckin, 657 | "trigger_enrollment_complete": in.General.TriggerEnrollmentComplete, 658 | "trigger_login": in.General.TriggerLogin, 659 | "trigger_network_state_changed": in.General.TriggerNetworkStateChanged, 660 | "trigger_startup": in.General.TriggerStartup, 661 | "trigger_other": in.General.TriggerOther, 662 | "frequency": in.General.Frequency, 663 | "retry_event": in.General.RetryEvent, 664 | "retry_attempts": in.General.RetryAttempts, 665 | "notify_on_each_failed_retry": in.General.NotifyOnEachFailedRetry, 666 | "location_user_only": in.General.LocationUserOnly, 667 | "target_drive": in.General.TargetDrive, 668 | "offline": in.General.Offline, 669 | "network_requirements": in.General.NetworkRequirements, 670 | "category": []interface{}{ 671 | map[string]interface{}{ 672 | "id": in.General.Category.ID, 673 | "name": in.General.Category.Name, 674 | }, 675 | }, 676 | "site": []interface{}{ 677 | map[string]interface{}{ 678 | "id": in.General.Site.ID, 679 | "name": in.General.Site.Name, 680 | }, 681 | }, 682 | } 683 | 684 | if in.General.DateTimeLimitations != (jamf.PolicyGeneralDateTimeLimitations{}) { 685 | general["date_time_limitations"] = []interface{}{ 686 | map[string]interface{}{ 687 | "activation_date": in.General.DateTimeLimitations.ActivationDate, 688 | "activation_date_epoch": in.General.DateTimeLimitations.ActivationDateEpoch, 689 | "activation_date_utc": in.General.DateTimeLimitations.ActivationDateUtc, 690 | "expiration_date": in.General.DateTimeLimitations.ExpirationDate, 691 | "expiration_date_epoch": in.General.DateTimeLimitations.ExpirationDateEpoch, 692 | "expiration_date_utc": in.General.DateTimeLimitations.ExpirationDateUtc, 693 | "no_execute_on": in.General.DateTimeLimitations.NoExecuteOn, 694 | "no_execute_start": in.General.DateTimeLimitations.NoExecuteStart, 695 | "no_execute_end": in.General.DateTimeLimitations.NoExecuteEnd, 696 | }, 697 | } 698 | } 699 | 700 | if in.General.NetworkLimitations != (jamf.PolicyGeneralNetworkLimitations{}) { 701 | general["network_limitations"] = []interface{}{ 702 | map[string]interface{}{ 703 | "minimum_network_connection": in.General.NetworkLimitations.MinimumNetworkConnection, 704 | "any_ip_address": in.General.NetworkLimitations.AnyIpAddress, 705 | "network_segments": in.General.NetworkLimitations.NetworkSegments, 706 | }, 707 | } 708 | } 709 | 710 | if in.General.OverrideDefaultSettings != (jamf.PolicyGeneralOverrideDefaultSettings{}) { 711 | general["override_default_settings"] = []interface{}{ 712 | map[string]interface{}{ 713 | "target_drive": in.General.OverrideDefaultSettings.TargetDrive, 714 | "distribution_point": in.General.OverrideDefaultSettings.DistributionPoint, 715 | "force_afp_smb": in.General.OverrideDefaultSettings.ForceAfpSmb, 716 | "sus": in.General.OverrideDefaultSettings.Sus, 717 | "netboot_server": in.General.OverrideDefaultSettings.NetbootServer, 718 | }, 719 | } 720 | } 721 | d.Set("general", []interface{}{general}) 722 | 723 | // Scope 724 | scope := map[string]interface{}{ 725 | "all_computers": in.Scope.AllComputers, 726 | } 727 | 728 | // Scope - Computers 729 | computers := []interface{}{} 730 | for _, v := range in.Scope.Computers { 731 | computers = append(computers, map[string]interface{}{ 732 | "id": v.ID, 733 | "name": v.Name, 734 | "udid": v.UDID, 735 | }) 736 | } 737 | scope["computer"] = computers 738 | 739 | // Scope - Computer Groups 740 | computerGroups := []interface{}{} 741 | for _, v := range in.Scope.ComputerGroups { 742 | computerGroups = append(computerGroups, map[string]interface{}{ 743 | "id": v.ID, 744 | "name": v.Name, 745 | }) 746 | } 747 | scope["computer_group"] = computerGroups 748 | 749 | // Scope - Buildings 750 | buildings := []interface{}{} 751 | for _, v := range in.Scope.Buildings { 752 | buildings = append(buildings, map[string]interface{}{ 753 | "id": v.ID, 754 | "name": v.Name, 755 | }) 756 | } 757 | scope["building"] = buildings 758 | 759 | // Scope - Departments 760 | departments := []interface{}{} 761 | for _, v := range in.Scope.Departments { 762 | departments = append(departments, map[string]interface{}{ 763 | "id": v.ID, 764 | "name": v.Name, 765 | }) 766 | } 767 | scope["department"] = departments 768 | d.Set("scope", []interface{}{scope}) 769 | 770 | // Self Service 771 | selfService := map[string]interface{}{ 772 | "use_for_self_service": in.SelfService.UseForSelfService, 773 | "self_service_display_name": in.SelfService.SelfServiceDisplayName, 774 | "install_button_text": in.SelfService.InstallButtonText, 775 | "reinstall_button_text": in.SelfService.ReinstallButtonText, 776 | "self_service_description": in.SelfService.SelfServiceDescription, 777 | "force_users_to_view_description": in.SelfService.ForceUsersToViewDescription, 778 | "feature_on_main_page": in.SelfService.FeatureOnMainPage, 779 | "self_service_icon": []interface{}{ 780 | map[string]interface{}{ 781 | "id": in.SelfService.SelfServiceIcon.ID, 782 | "filename": in.SelfService.SelfServiceIcon.Filename, 783 | "uri": in.SelfService.SelfServiceIcon.URI, 784 | }, 785 | }, 786 | } 787 | 788 | // Self Service - Category 789 | if len(in.SelfService.SelfServiceCategories) != 0 { 790 | selfServiceCategory := []interface{}{} 791 | for _, v := range in.SelfService.SelfServiceCategories { 792 | selfServiceCategory = append(selfServiceCategory, map[string]interface{}{ 793 | "id": v.ID, 794 | "name": v.Name, 795 | "display_in": v.DisplayIn, 796 | "feature_in": v.FeatureIn, 797 | }) 798 | } 799 | selfService["self_service_category"] = selfServiceCategory 800 | } 801 | d.Set("self_service", []interface{}{selfService}) 802 | 803 | // Packages 804 | if len(in.PackageConfiguration.Packages) != 0 { 805 | packages := []interface{}{} 806 | for _, v := range in.PackageConfiguration.Packages { 807 | packages = append(packages, map[string]interface{}{ 808 | "id": v.ID, 809 | "name": v.Name, 810 | "action": v.Action, 811 | "fut": v.FillUserTemplate, 812 | "feu": v.FillExistingUsers, 813 | "update_autorun": v.UpdateAutorun, 814 | }) 815 | } 816 | d.Set("package", packages) 817 | } 818 | 819 | // Scripts 820 | if len(in.ScriptsConfiguration.Scripts) != 0 { 821 | scripts := []interface{}{} 822 | for _, v := range in.ScriptsConfiguration.Scripts { 823 | scripts = append(scripts, map[string]interface{}{ 824 | "id": v.ID, 825 | "name": v.Name, 826 | "priority": v.Priority, 827 | "parameter4": v.Parameter4, 828 | "parameter5": v.Parameter5, 829 | "parameter6": v.Parameter6, 830 | "parameter7": v.Parameter7, 831 | "parameter8": v.Parameter8, 832 | "parameter9": v.Parameter9, 833 | "parameter10": v.Parameter10, 834 | "parameter11": v.Parameter11, 835 | }) 836 | } 837 | d.Set("script", scripts) 838 | } 839 | 840 | // Reboot 841 | if in.Reboot != (jamf.PolicyReboot{}) { 842 | d.Set("reboot", []interface{}{ 843 | map[string]interface{}{ 844 | "message": in.Reboot.Message, 845 | "startup_disk": in.Reboot.StartupDisk, 846 | "specify_startup": in.Reboot.SpecifyStartup, 847 | "no_user_logged_in": in.Reboot.NoUserLoggedIn, 848 | "user_logged_in": in.Reboot.UserLoggedIn, 849 | "minutes_until_reboot": in.Reboot.MinutesUntilReboot, 850 | "start_reboot_timer_immediately": in.Reboot.StartRebootTimerImmediately, 851 | "file_vault_2_reboot": in.Reboot.FileVault2Reboot, 852 | }, 853 | }) 854 | } 855 | 856 | // Maintenance 857 | if in.Maintenance != (jamf.PolicyMaintenance{}) { 858 | d.Set("maintenance", []interface{}{ 859 | map[string]interface{}{ 860 | "recon": in.Maintenance.Recon, 861 | "reset_name": in.Maintenance.ResetName, 862 | "install_all_cached_packages": in.Maintenance.InstallAllCachedPackages, 863 | "heal": in.Maintenance.Heal, 864 | "prebindings": in.Maintenance.Prebindings, 865 | "permissions": in.Maintenance.Permissions, 866 | "byhost": in.Maintenance.Byhost, 867 | "system_cache": in.Maintenance.SystemCache, 868 | "user_cache": in.Maintenance.UserCache, 869 | "verify": in.Maintenance.Verify, 870 | }, 871 | }) 872 | } 873 | 874 | // Files and Processses 875 | if in.FilesAndProcesses != (jamf.PolicyFilesAndProcesses{}) { 876 | d.Set("files_and_processes", []interface{}{ 877 | map[string]interface{}{ 878 | "search_by_path": in.FilesAndProcesses.SearchByPath, 879 | "delete_file": in.FilesAndProcesses.DeleteFile, 880 | "locate_file": in.FilesAndProcesses.LocateFile, 881 | "update_locate_database": in.FilesAndProcesses.UpdateLocateDatabase, 882 | "spotlight_search": in.FilesAndProcesses.SpotlightSearch, 883 | "search_for_process": in.FilesAndProcesses.SearchForProcess, 884 | "kill_process": in.FilesAndProcesses.KillProcess, 885 | "run_command": in.FilesAndProcesses.RunCommand, 886 | }, 887 | }) 888 | } 889 | 890 | // User Interaction 891 | if in.UserInteraction != (jamf.PolicyUserInteraction{}) { 892 | d.Set("user_interaction", []interface{}{ 893 | map[string]interface{}{ 894 | "message_start": in.UserInteraction.MessageStart, 895 | "allow_users_to_defer": in.UserInteraction.AllowUsersToDefer, 896 | "allow_deferral_until_utc": in.UserInteraction.AllowDeferralUntilUtc, 897 | "allow_deferral_minutes": in.UserInteraction.AllowDeferralMinutes, 898 | "message_finish": in.UserInteraction.MessageFinish, 899 | }, 900 | }) 901 | } 902 | 903 | return 904 | } 905 | -------------------------------------------------------------------------------- /jamf/resource_jamf_policy.go: -------------------------------------------------------------------------------- 1 | package jamf 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strconv" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | "github.com/sioncojp/go-jamf-api" 11 | ) 12 | 13 | func resourceJamfPolicy() *schema.Resource { 14 | return &schema.Resource{ 15 | CreateContext: resourceJamfPolicyCreate, 16 | ReadContext: resourceJamfPolicyRead, 17 | UpdateContext: resourceJamfPolicyUpdate, 18 | DeleteContext: resourceJamfPolicyDelete, 19 | Importer: &schema.ResourceImporter{ 20 | StateContext: importJamfPolicyState, 21 | }, 22 | Schema: map[string]*schema.Schema{ 23 | "general": { 24 | Type: schema.TypeSet, 25 | Required: true, 26 | MaxItems: 1, 27 | Elem: &schema.Resource{ 28 | Schema: map[string]*schema.Schema{ 29 | "id": { 30 | Type: schema.TypeInt, 31 | Computed: true, 32 | }, 33 | "name": { 34 | Type: schema.TypeString, 35 | Required: true, 36 | }, 37 | "enabled": { 38 | Type: schema.TypeBool, 39 | Optional: true, 40 | Default: false, 41 | }, 42 | "trigger": { 43 | Type: schema.TypeString, 44 | Optional: true, 45 | Default: "EVENT", 46 | }, 47 | "trigger_checkin": { 48 | Type: schema.TypeBool, 49 | Optional: true, 50 | Default: false, 51 | }, 52 | "trigger_enrollment_complete": { 53 | Type: schema.TypeBool, 54 | Optional: true, 55 | Default: false, 56 | }, 57 | "trigger_login": { 58 | Type: schema.TypeBool, 59 | Optional: true, 60 | Default: false, 61 | }, 62 | "trigger_logout": { 63 | Type: schema.TypeBool, 64 | Optional: true, 65 | Default: false, 66 | }, 67 | "trigger_network_state_changed": { 68 | Type: schema.TypeBool, 69 | Optional: true, 70 | Default: false, 71 | }, 72 | "trigger_startup": { 73 | Type: schema.TypeBool, 74 | Optional: true, 75 | Default: false, 76 | }, 77 | "trigger_other": { 78 | Type: schema.TypeString, 79 | Optional: true, 80 | }, 81 | "frequency": { 82 | Type: schema.TypeString, 83 | Optional: true, 84 | Default: "Once per computer", 85 | }, 86 | "retry_event": { 87 | Type: schema.TypeString, 88 | Optional: true, 89 | Default: "none", 90 | }, 91 | "retry_attempts": { 92 | Type: schema.TypeInt, 93 | Optional: true, 94 | Default: -1, 95 | }, 96 | "notify_on_each_failed_retry": { 97 | Type: schema.TypeBool, 98 | Optional: true, 99 | Default: false, 100 | }, 101 | "location_user_only": { 102 | Type: schema.TypeBool, 103 | Optional: true, 104 | Default: false, 105 | }, 106 | "target_drive": { 107 | Type: schema.TypeString, 108 | Optional: true, 109 | Default: "/", 110 | }, 111 | "offline": { 112 | Type: schema.TypeBool, 113 | Optional: true, 114 | Default: false, 115 | }, 116 | "network_requirements": { 117 | Type: schema.TypeString, 118 | Optional: true, 119 | Default: "Any", 120 | }, 121 | "category": { 122 | Type: schema.TypeSet, 123 | Required: true, 124 | MaxItems: 1, 125 | Elem: &schema.Resource{ 126 | Schema: map[string]*schema.Schema{ 127 | "id": { 128 | Type: schema.TypeString, 129 | Required: true, 130 | }, 131 | "name": { 132 | Type: schema.TypeString, 133 | Required: true, 134 | }, 135 | }, 136 | }, 137 | }, 138 | "date_time_limitations": { 139 | Type: schema.TypeSet, 140 | Optional: true, 141 | MaxItems: 1, 142 | Elem: &schema.Resource{ 143 | Schema: map[string]*schema.Schema{ 144 | "activation_date": { 145 | Type: schema.TypeString, 146 | Computed: true, 147 | }, 148 | "activation_date_epoch": { 149 | Type: schema.TypeInt, 150 | Optional: true, 151 | Default: 0, 152 | }, 153 | "activation_date_utc": { 154 | Type: schema.TypeString, 155 | Computed: true, 156 | }, 157 | "expiration_date": { 158 | Type: schema.TypeString, 159 | Computed: true, 160 | }, 161 | "expiration_date_epoch": { 162 | Type: schema.TypeInt, 163 | Optional: true, 164 | Default: 0, 165 | }, 166 | "expiration_date_utc": { 167 | Type: schema.TypeString, 168 | Computed: true, 169 | }, 170 | "no_execute_on": { 171 | Type: schema.TypeString, 172 | Optional: true, 173 | }, 174 | "no_execute_start": { 175 | Type: schema.TypeString, 176 | Optional: true, 177 | }, 178 | "no_execute_end": { 179 | Type: schema.TypeString, 180 | Optional: true, 181 | }, 182 | }, 183 | }, 184 | }, 185 | "network_limitations": { 186 | Type: schema.TypeSet, 187 | Required: true, 188 | MaxItems: 1, 189 | Elem: &schema.Resource{ 190 | Schema: map[string]*schema.Schema{ 191 | "minimum_network_connection": { 192 | Type: schema.TypeString, 193 | Optional: true, 194 | Default: "No Minimum", 195 | }, 196 | "any_ip_address": { 197 | Type: schema.TypeBool, 198 | Optional: true, 199 | Default: true, 200 | }, 201 | "network_segments": { 202 | Type: schema.TypeString, 203 | Optional: true, 204 | }, 205 | }, 206 | }, 207 | }, 208 | "override_default_settings": { 209 | Type: schema.TypeSet, 210 | Required: true, 211 | MaxItems: 1, 212 | Elem: &schema.Resource{ 213 | Schema: map[string]*schema.Schema{ 214 | "target_drive": { 215 | Type: schema.TypeString, 216 | Optional: true, 217 | Default: "default", 218 | }, 219 | "distribution_point": { 220 | Type: schema.TypeString, 221 | Optional: true, 222 | }, 223 | "force_afp_smb": { 224 | Type: schema.TypeBool, 225 | Optional: true, 226 | Default: false, 227 | }, 228 | "sus": { 229 | Type: schema.TypeString, 230 | Optional: true, 231 | Default: "default", 232 | }, 233 | "netboot_server": { 234 | Type: schema.TypeString, 235 | Optional: true, 236 | Default: "current", 237 | }, 238 | }, 239 | }, 240 | }, 241 | "site": { 242 | Type: schema.TypeSet, 243 | Required: true, 244 | MaxItems: 1, 245 | Elem: &schema.Resource{ 246 | Schema: map[string]*schema.Schema{ 247 | "id": { 248 | Type: schema.TypeInt, 249 | Optional: true, 250 | Default: -1, 251 | }, 252 | "name": { 253 | Type: schema.TypeString, 254 | Computed: true, 255 | }, 256 | }, 257 | }, 258 | }, 259 | }, 260 | }, 261 | }, 262 | "scope": { 263 | Type: schema.TypeSet, 264 | Required: true, 265 | MaxItems: 1, 266 | Elem: &schema.Resource{ 267 | Schema: map[string]*schema.Schema{ 268 | "all_computers": { 269 | Type: schema.TypeBool, 270 | Optional: true, 271 | Default: false, 272 | }, 273 | "computer": { 274 | Type: schema.TypeSet, 275 | Optional: true, 276 | Elem: &schema.Resource{ 277 | Schema: map[string]*schema.Schema{ 278 | "id": { 279 | Type: schema.TypeInt, 280 | Required: true, 281 | }, 282 | "name": { 283 | Type: schema.TypeString, 284 | Computed: true, 285 | }, 286 | "udid": { 287 | Type: schema.TypeString, 288 | Computed: true, 289 | }, 290 | }, 291 | }, 292 | }, 293 | "computer_group": { 294 | Type: schema.TypeSet, 295 | Optional: true, 296 | Elem: &schema.Resource{ 297 | Schema: map[string]*schema.Schema{ 298 | "id": { 299 | Type: schema.TypeInt, 300 | Required: true, 301 | }, 302 | "name": { 303 | Type: schema.TypeString, 304 | Computed: true, 305 | }, 306 | }, 307 | }, 308 | }, 309 | "building": { 310 | Type: schema.TypeSet, 311 | Optional: true, 312 | Elem: &schema.Resource{ 313 | Schema: map[string]*schema.Schema{ 314 | "id": { 315 | Type: schema.TypeInt, 316 | Required: true, 317 | }, 318 | "name": { 319 | Type: schema.TypeString, 320 | Computed: true, 321 | }, 322 | }, 323 | }, 324 | }, 325 | "department": { 326 | Type: schema.TypeSet, 327 | Optional: true, 328 | Elem: &schema.Resource{ 329 | Schema: map[string]*schema.Schema{ 330 | "id": { 331 | Type: schema.TypeInt, 332 | Required: true, 333 | }, 334 | "name": { 335 | Type: schema.TypeString, 336 | Computed: true, 337 | }, 338 | }, 339 | }, 340 | }, 341 | }, 342 | }, 343 | }, 344 | "self_service": { 345 | Type: schema.TypeSet, 346 | Required: true, 347 | MaxItems: 1, 348 | Elem: &schema.Resource{ 349 | Schema: map[string]*schema.Schema{ 350 | "use_for_self_service": { 351 | Type: schema.TypeBool, 352 | Optional: true, 353 | Default: false, 354 | }, 355 | "self_service_display_name": { 356 | Type: schema.TypeString, 357 | Optional: true, 358 | }, 359 | "install_button_text": { 360 | Type: schema.TypeString, 361 | Optional: true, 362 | Default: "Install", 363 | }, 364 | "reinstall_button_text": { 365 | Type: schema.TypeString, 366 | Optional: true, 367 | Default: "Reinstall", 368 | }, 369 | "self_service_description": { 370 | Type: schema.TypeString, 371 | Optional: true, 372 | }, 373 | "force_users_to_view_description": { 374 | Type: schema.TypeBool, 375 | Optional: true, 376 | Default: false, 377 | }, 378 | "feature_on_main_page": { 379 | Type: schema.TypeBool, 380 | Optional: true, 381 | Default: false, 382 | }, 383 | "self_service_icon": { 384 | Type: schema.TypeSet, 385 | Required: true, 386 | MaxItems: 1, 387 | Elem: &schema.Resource{ 388 | Schema: map[string]*schema.Schema{ 389 | "id": { 390 | Type: schema.TypeInt, 391 | Optional: true, 392 | Default: 0, 393 | }, 394 | "filename": { 395 | Type: schema.TypeString, 396 | Computed: true, 397 | }, 398 | "uri": { 399 | Type: schema.TypeString, 400 | Computed: true, 401 | }, 402 | }, 403 | }, 404 | }, 405 | "self_service_category": { 406 | Type: schema.TypeSet, 407 | Optional: true, 408 | Elem: &schema.Resource{ 409 | Schema: map[string]*schema.Schema{ 410 | "id": { 411 | Type: schema.TypeInt, 412 | Required: true, 413 | }, 414 | "name": { 415 | Type: schema.TypeString, 416 | Computed: true, 417 | }, 418 | "display_in": { 419 | Type: schema.TypeBool, 420 | Optional: true, 421 | }, 422 | "feature_in": { 423 | Type: schema.TypeBool, 424 | Optional: true, 425 | }, 426 | }, 427 | }, 428 | }, 429 | }, 430 | }, 431 | }, 432 | "package": { 433 | Type: schema.TypeSet, 434 | Optional: true, 435 | Elem: &schema.Resource{ 436 | Schema: map[string]*schema.Schema{ 437 | "id": { 438 | Type: schema.TypeInt, 439 | Required: true, 440 | }, 441 | "name": { 442 | Type: schema.TypeString, 443 | Computed: true, 444 | }, 445 | "action": { 446 | Type: schema.TypeString, 447 | Optional: true, 448 | Default: "INSTALL", 449 | }, 450 | "fut": { 451 | Type: schema.TypeBool, 452 | Optional: true, 453 | }, 454 | "feu": { 455 | Type: schema.TypeBool, 456 | Optional: true, 457 | }, 458 | "update_autorun": { 459 | Type: schema.TypeBool, 460 | Optional: true, 461 | }, 462 | }, 463 | }, 464 | }, 465 | "script": { 466 | Type: schema.TypeSet, 467 | Optional: true, 468 | Elem: &schema.Resource{ 469 | Schema: map[string]*schema.Schema{ 470 | "id": { 471 | Type: schema.TypeString, 472 | Optional: true, 473 | }, 474 | "name": { 475 | Type: schema.TypeString, 476 | Computed: true, 477 | }, 478 | "priority": { 479 | Type: schema.TypeString, 480 | Optional: true, 481 | Default: "AFTER", 482 | }, 483 | "parameter4": { 484 | Type: schema.TypeString, 485 | Optional: true, 486 | }, 487 | "parameter5": { 488 | Type: schema.TypeString, 489 | Optional: true, 490 | }, 491 | "parameter6": { 492 | Type: schema.TypeString, 493 | Optional: true, 494 | }, 495 | "parameter7": { 496 | Type: schema.TypeString, 497 | Optional: true, 498 | }, 499 | "parameter8": { 500 | Type: schema.TypeString, 501 | Optional: true, 502 | }, 503 | "parameter9": { 504 | Type: schema.TypeString, 505 | Optional: true, 506 | }, 507 | "parameter10": { 508 | Type: schema.TypeString, 509 | Optional: true, 510 | }, 511 | "parameter11": { 512 | Type: schema.TypeString, 513 | Optional: true, 514 | }, 515 | }, 516 | }, 517 | }, 518 | "reboot": { 519 | Type: schema.TypeSet, 520 | Required: true, 521 | Elem: &schema.Resource{ 522 | Schema: map[string]*schema.Schema{ 523 | "message": { 524 | Type: schema.TypeString, 525 | Optional: true, 526 | }, 527 | "startup_disk": { 528 | Type: schema.TypeString, 529 | Optional: true, 530 | Default: "Current Startup Disk", 531 | }, 532 | "specify_startup": { 533 | Type: schema.TypeString, 534 | Optional: true, 535 | }, 536 | "no_user_logged_in": { 537 | Type: schema.TypeString, 538 | Optional: true, 539 | Default: "Do not restart", 540 | }, 541 | "user_logged_in": { 542 | Type: schema.TypeString, 543 | Optional: true, 544 | Default: "Do not restart", 545 | }, 546 | "minutes_until_reboot": { 547 | Type: schema.TypeInt, 548 | Optional: true, 549 | Default: 5, 550 | }, 551 | "start_reboot_timer_immediately": { 552 | Type: schema.TypeBool, 553 | Optional: true, 554 | }, 555 | "file_vault_2_reboot": { 556 | Type: schema.TypeBool, 557 | Optional: true, 558 | }, 559 | }, 560 | }, 561 | }, 562 | "maintenance": { 563 | Type: schema.TypeSet, 564 | Optional: true, 565 | Elem: &schema.Resource{ 566 | Schema: map[string]*schema.Schema{ 567 | "recon": { 568 | Type: schema.TypeBool, 569 | Optional: true, 570 | }, 571 | "reset_name": { 572 | Type: schema.TypeBool, 573 | Optional: true, 574 | }, 575 | "install_all_cached_packages": { 576 | Type: schema.TypeBool, 577 | Optional: true, 578 | }, 579 | "heal": { 580 | Type: schema.TypeBool, 581 | Optional: true, 582 | }, 583 | "prebindings": { 584 | Type: schema.TypeBool, 585 | Optional: true, 586 | }, 587 | "permissions": { 588 | Type: schema.TypeBool, 589 | Optional: true, 590 | }, 591 | "byhost": { 592 | Type: schema.TypeBool, 593 | Optional: true, 594 | }, 595 | "system_cache": { 596 | Type: schema.TypeBool, 597 | Optional: true, 598 | }, 599 | "user_cache": { 600 | Type: schema.TypeBool, 601 | Optional: true, 602 | }, 603 | "verify": { 604 | Type: schema.TypeBool, 605 | Optional: true, 606 | }, 607 | }, 608 | }, 609 | }, 610 | "files_and_processes": { 611 | Type: schema.TypeSet, 612 | Optional: true, 613 | Elem: &schema.Resource{ 614 | Schema: map[string]*schema.Schema{ 615 | "search_by_path": { 616 | Type: schema.TypeString, 617 | Optional: true, 618 | }, 619 | "delete_file": { 620 | Type: schema.TypeBool, 621 | Optional: true, 622 | }, 623 | "locate_file": { 624 | Type: schema.TypeString, 625 | Optional: true, 626 | }, 627 | "update_locate_database": { 628 | Type: schema.TypeBool, 629 | Optional: true, 630 | }, 631 | "spotlight_search": { 632 | Type: schema.TypeString, 633 | Optional: true, 634 | }, 635 | "search_for_process": { 636 | Type: schema.TypeString, 637 | Optional: true, 638 | }, 639 | "kill_process": { 640 | Type: schema.TypeBool, 641 | Optional: true, 642 | }, 643 | "run_command": { 644 | Type: schema.TypeString, 645 | Optional: true, 646 | }, 647 | }, 648 | }, 649 | }, 650 | "user_interaction": { 651 | Type: schema.TypeSet, 652 | Optional: true, 653 | Elem: &schema.Resource{ 654 | Schema: map[string]*schema.Schema{ 655 | "message_start": { 656 | Type: schema.TypeString, 657 | Optional: true, 658 | }, 659 | "allow_users_to_defer": { 660 | Type: schema.TypeBool, 661 | Optional: true, 662 | }, 663 | "allow_deferral_until_utc": { 664 | Type: schema.TypeString, 665 | Optional: true, 666 | }, 667 | "allow_deferral_minutes": { 668 | Type: schema.TypeInt, 669 | Optional: true, 670 | }, 671 | "message_finish": { 672 | Type: schema.TypeString, 673 | Optional: true, 674 | }, 675 | }, 676 | }, 677 | }, 678 | }, 679 | } 680 | } 681 | 682 | func buildJamfPolicyStruct(d *schema.ResourceData) *jamf.Policy { 683 | var out jamf.Policy 684 | 685 | // General 686 | id, _ := strconv.Atoi(d.Id()) 687 | out.General.ID = id 688 | if g, ok := d.GetOk("general"); ok { 689 | v := g.(*schema.Set).List() 690 | general := v[0].(map[string]interface{}) 691 | 692 | out.General.Name = general["name"].(string) 693 | 694 | if val, ok := general["enabled"]; ok { 695 | out.General.Enabled = val.(bool) 696 | } 697 | if val, ok := general["trigger"]; ok { 698 | out.General.Trigger = val.(string) 699 | } 700 | if val, ok := general["trigger_checkin"]; ok { 701 | out.General.TriggerCheckin = val.(bool) 702 | } 703 | if val, ok := general["trigger_enrollment_complete"]; ok { 704 | out.General.TriggerEnrollmentComplete = val.(bool) 705 | } 706 | if val, ok := general["trigger_logout"]; ok { 707 | out.General.TriggerLogout = val.(bool) 708 | } 709 | if val, ok := general["trigger_network_state_changed"]; ok { 710 | out.General.TriggerNetworkStateChanged = val.(bool) 711 | } 712 | if val, ok := general["trigger_startup"]; ok { 713 | out.General.TriggerStartup = val.(bool) 714 | } 715 | if val, ok := general["trigger_other"]; ok { 716 | out.General.TriggerOther = val.(string) 717 | } 718 | if val, ok := general["frequency"]; ok { 719 | out.General.Frequency = val.(string) 720 | } 721 | if val, ok := general["retry_event"]; ok { 722 | out.General.RetryEvent = val.(string) 723 | } 724 | if val, ok := general["retry_attempts"]; ok { 725 | out.General.RetryAttempts = val.(int) 726 | } 727 | if val, ok := general["notify_on_each_failed_retry"]; ok { 728 | out.General.NotifyOnEachFailedRetry = val.(bool) 729 | } 730 | if val, ok := general["location_user_only"]; ok { 731 | out.General.LocationUserOnly = val.(bool) 732 | } 733 | if val, ok := general["target_drive"]; ok { 734 | out.General.TargetDrive = val.(string) 735 | } 736 | if val, ok := general["offline"]; ok { 737 | out.General.Offline = val.(bool) 738 | } 739 | if val, ok := general["network_requirements"]; ok { 740 | out.General.NetworkRequirements = val.(string) 741 | } 742 | 743 | // General - Category 744 | if v, ok := general["category"]; ok { 745 | categoryList := v.(*schema.Set).List() 746 | category := categoryList[0].(map[string]interface{}) 747 | if val, ok := category["name"].(string); ok { 748 | out.General.Category.Name = val 749 | } 750 | if val, ok := category["id"].(string); ok { 751 | out.General.Category.ID = val 752 | } 753 | } 754 | 755 | // General - Date and Time Limitations 756 | if v, ok := general["date_time_limitations"]; ok { 757 | dateAndTimeLimitationList := v.(*schema.Set).List() 758 | if len(dateAndTimeLimitationList) == 1 { 759 | dateAndTimeLimitation := dateAndTimeLimitationList[0].(map[string]interface{}) 760 | if val, ok := dateAndTimeLimitation["activation_date"].(string); ok { 761 | out.General.DateTimeLimitations.ActivationDate = val 762 | } 763 | if val, ok := dateAndTimeLimitation["activation_date_epoch"].(int); ok { 764 | out.General.DateTimeLimitations.ActivationDateEpoch = val 765 | } 766 | if val, ok := dateAndTimeLimitation["activation_date_utc"].(string); ok { 767 | out.General.DateTimeLimitations.ActivationDateUtc = val 768 | } 769 | if val, ok := dateAndTimeLimitation["expiration_date"].(string); ok { 770 | out.General.DateTimeLimitations.ExpirationDate = val 771 | } 772 | if val, ok := dateAndTimeLimitation["expiration_date_epoch"].(int); ok { 773 | out.General.DateTimeLimitations.ExpirationDateEpoch = val 774 | } 775 | if val, ok := dateAndTimeLimitation["expiration_date_utc"].(string); ok { 776 | out.General.DateTimeLimitations.ExpirationDateUtc = val 777 | } 778 | if val, ok := dateAndTimeLimitation["no_execute_on"].(string); ok { 779 | out.General.DateTimeLimitations.NoExecuteOn = val 780 | } 781 | if val, ok := dateAndTimeLimitation["no_execute_start"].(string); ok { 782 | out.General.DateTimeLimitations.NoExecuteStart = val 783 | } 784 | if val, ok := dateAndTimeLimitation["no_execute_end"].(string); ok { 785 | out.General.DateTimeLimitations.NoExecuteEnd = val 786 | } 787 | } 788 | } 789 | 790 | // General - Network Limitations 791 | if v, ok := general["network_limitations"]; ok { 792 | networkLimitationList := v.(*schema.Set).List() 793 | networkLimitation := networkLimitationList[0].(map[string]interface{}) 794 | if val, ok := networkLimitation["minimum_network_connection"].(string); ok { 795 | out.General.NetworkLimitations.MinimumNetworkConnection = val 796 | } 797 | if val, ok := networkLimitation["any_ip_address"].(bool); ok { 798 | out.General.NetworkLimitations.AnyIpAddress = val 799 | } 800 | if val, ok := networkLimitation["network_segments"].(string); ok { 801 | out.General.NetworkLimitations.NetworkSegments = val 802 | } 803 | } 804 | 805 | // General - Override Default Settings 806 | if v, ok := general["override_default_settings"]; ok { 807 | overrideDefaultSettingsList := v.(*schema.Set).List() 808 | overrideDefaultSettings := overrideDefaultSettingsList[0].(map[string]interface{}) 809 | if val, ok := overrideDefaultSettings["target_drive"].(string); ok { 810 | out.General.OverrideDefaultSettings.TargetDrive = val 811 | } 812 | if val, ok := overrideDefaultSettings["distribution_point"].(string); ok { 813 | out.General.OverrideDefaultSettings.DistributionPoint = val 814 | } 815 | if val, ok := overrideDefaultSettings["force_afp_smb"].(bool); ok { 816 | out.General.OverrideDefaultSettings.ForceAfpSmb = val 817 | } 818 | if val, ok := overrideDefaultSettings["sus"].(string); ok { 819 | out.General.OverrideDefaultSettings.Sus = val 820 | } 821 | if val, ok := overrideDefaultSettings["netboot_server"].(string); ok { 822 | out.General.OverrideDefaultSettings.NetbootServer = val 823 | } 824 | } 825 | 826 | // General - Site 827 | if v, ok := general["site"]; ok { 828 | siteList := v.(*schema.Set).List() 829 | site := siteList[0].(map[string]interface{}) 830 | if val, ok := site["name"].(string); ok { 831 | out.General.Site.Name = val 832 | } 833 | if val, ok := site["id"].(int); ok { 834 | out.General.Site.ID = val 835 | } 836 | } 837 | } 838 | 839 | // Scope 840 | if s, ok := d.GetOk("scope"); ok { 841 | v := s.(*schema.Set).List() 842 | scope := v[0].(map[string]interface{}) 843 | 844 | if val, ok := scope["all_computers"]; ok { 845 | out.Scope.AllComputers = val.(bool) 846 | } 847 | 848 | // Scope - Computers 849 | if v, ok := scope["computer"]; ok { 850 | computers := v.(*schema.Set).List() 851 | computerList := []jamf.ComputerPolicyList{} 852 | for _, c := range computers { 853 | computerData := c.(map[string]interface{}) 854 | computer := jamf.ComputerPolicyList{} 855 | if val, ok := computerData["id"].(int); ok { 856 | computer.ID = val 857 | } 858 | computerList = append(computerList, computer) 859 | } 860 | out.Scope.Computers = computerList 861 | } 862 | 863 | // Scope - Computer Groups 864 | if v, ok := scope["computer_group"]; ok { 865 | computerGroups := v.(*schema.Set).List() 866 | computerGroupList := []jamf.ComputerGroupListResponse{} 867 | for _, c := range computerGroups { 868 | computerGroupData := c.(map[string]interface{}) 869 | computerGroup := jamf.ComputerGroupListResponse{} 870 | if val, ok := computerGroupData["id"].(int); ok { 871 | computerGroup.ID = val 872 | } 873 | computerGroupList = append(computerGroupList, computerGroup) 874 | } 875 | out.Scope.ComputerGroups = computerGroupList 876 | } 877 | 878 | // Scope - Buildings 879 | if v, ok := scope["building"]; ok { 880 | buildings := v.(*schema.Set).List() 881 | buildingList := []jamf.BuildingPolicyList{} 882 | for _, c := range buildings { 883 | buildingData := c.(map[string]interface{}) 884 | building := jamf.BuildingPolicyList{} 885 | if val, ok := buildingData["id"].(int); ok { 886 | building.ID = val 887 | } 888 | buildingList = append(buildingList, building) 889 | } 890 | out.Scope.Buildings = buildingList 891 | } 892 | 893 | // Scope - Departments 894 | if v, ok := scope["department"]; ok { 895 | departments := v.(*schema.Set).List() 896 | departmentList := []jamf.DepartmentPolicyList{} 897 | for _, c := range departments { 898 | departmentData := c.(map[string]interface{}) 899 | department := jamf.DepartmentPolicyList{} 900 | if val, ok := departmentData["id"].(int); ok { 901 | department.ID = val 902 | } 903 | departmentList = append(departmentList, department) 904 | } 905 | out.Scope.Departments = departmentList 906 | } 907 | } 908 | 909 | // Self Service 910 | if s, ok := d.GetOk("self_service"); ok { 911 | v := s.(*schema.Set).List() 912 | selfService := v[0].(map[string]interface{}) 913 | 914 | if val, ok := selfService["use_for_self_service"]; ok { 915 | out.SelfService.UseForSelfService = val.(bool) 916 | } 917 | if val, ok := selfService["self_service_display_name"]; ok { 918 | out.SelfService.SelfServiceDisplayName = val.(string) 919 | } 920 | if val, ok := selfService["install_button_text"]; ok { 921 | out.SelfService.InstallButtonText = val.(string) 922 | } 923 | if val, ok := selfService["reinstall_button_text"]; ok { 924 | out.SelfService.ReinstallButtonText = val.(string) 925 | } 926 | if val, ok := selfService["self_service_description"]; ok { 927 | out.SelfService.SelfServiceDescription = val.(string) 928 | } 929 | if val, ok := selfService["force_users_to_view_description"]; ok { 930 | out.SelfService.ForceUsersToViewDescription = val.(bool) 931 | } 932 | if val, ok := selfService["feature_on_main_page"]; ok { 933 | out.SelfService.FeatureOnMainPage = val.(bool) 934 | } 935 | if v, ok := selfService["self_service_icon"]; ok { 936 | selfServiceIconList := v.(*schema.Set).List() 937 | selfServiceIcon := selfServiceIconList[0].(map[string]interface{}) 938 | if val, ok := selfServiceIcon["id"].(int); ok { 939 | out.SelfService.SelfServiceIcon.ID = val 940 | } 941 | } 942 | 943 | // Self Service - Category 944 | if v, ok := selfService["self_service_category"]; ok { 945 | selfServiceCategories := v.(*schema.Set).List() 946 | selfServiceCategoryList := []jamf.PolicySelfServiceCategory{} 947 | for _, c := range selfServiceCategories { 948 | selfServiceCategoryData := c.(map[string]interface{}) 949 | selfServiceCategory := jamf.PolicySelfServiceCategory{} 950 | if val, ok := selfServiceCategoryData["id"].(int); ok { 951 | selfServiceCategory.ID = val 952 | } 953 | if val, ok := selfServiceCategoryData["display_in"].(bool); ok { 954 | selfServiceCategory.DisplayIn = val 955 | } 956 | if val, ok := selfServiceCategoryData["feature_in"].(bool); ok { 957 | selfServiceCategory.FeatureIn = val 958 | } 959 | selfServiceCategoryList = append(selfServiceCategoryList, selfServiceCategory) 960 | } 961 | out.SelfService.SelfServiceCategories = selfServiceCategoryList 962 | } 963 | } 964 | 965 | // Packages 966 | if v, ok := d.GetOk("package"); ok { 967 | packages := v.(*schema.Set).List() 968 | packageList := []jamf.PolicyPackageConfigurationPackage{} 969 | for _, c := range packages { 970 | packageData := c.(map[string]interface{}) 971 | pkg := jamf.PolicyPackageConfigurationPackage{} 972 | if val, ok := packageData["id"].(int); ok { 973 | pkg.ID = val 974 | } 975 | if val, ok := packageData["action"].(string); ok { 976 | pkg.Action = val 977 | } 978 | if val, ok := packageData["fut"].(bool); ok { 979 | pkg.FillUserTemplate = val 980 | } 981 | if val, ok := packageData["feu"].(bool); ok { 982 | pkg.FillExistingUsers = val 983 | } 984 | if val, ok := packageData["update_autorun"].(bool); ok { 985 | pkg.UpdateAutorun = val 986 | } 987 | packageList = append(packageList, pkg) 988 | } 989 | out.PackageConfiguration.Packages = packageList 990 | } 991 | 992 | // Scripts 993 | if v, ok := d.GetOk("script"); ok { 994 | scripts := v.(*schema.Set).List() 995 | scriptList := []jamf.PolicyScript{} 996 | for _, c := range scripts { 997 | scriptData := c.(map[string]interface{}) 998 | script := jamf.PolicyScript{} 999 | if val, ok := scriptData["id"].(string); ok { 1000 | script.ID = val 1001 | } 1002 | if val, ok := scriptData["priority"].(string); ok { 1003 | script.Priority = val 1004 | } 1005 | if val, ok := scriptData["parameter4"].(string); ok { 1006 | script.Parameter4 = val 1007 | } 1008 | if val, ok := scriptData["parameter5"].(string); ok { 1009 | script.Parameter5 = val 1010 | } 1011 | if val, ok := scriptData["parameter6"].(string); ok { 1012 | script.Parameter6 = val 1013 | } 1014 | if val, ok := scriptData["parameter7"].(string); ok { 1015 | script.Parameter7 = val 1016 | } 1017 | if val, ok := scriptData["parameter8"].(string); ok { 1018 | script.Parameter8 = val 1019 | } 1020 | if val, ok := scriptData["parameter9"].(string); ok { 1021 | script.Parameter9 = val 1022 | } 1023 | if val, ok := scriptData["parameter10"].(string); ok { 1024 | script.Parameter10 = val 1025 | } 1026 | if val, ok := scriptData["parameter11"].(string); ok { 1027 | script.Parameter11 = val 1028 | } 1029 | scriptList = append(scriptList, script) 1030 | } 1031 | out.ScriptsConfiguration.Scripts = scriptList 1032 | } 1033 | 1034 | // Reboot 1035 | if r, ok := d.GetOk("reboot"); ok { 1036 | v := r.(*schema.Set).List() 1037 | reboot := v[0].(map[string]interface{}) 1038 | 1039 | if val, ok := reboot["message"]; ok { 1040 | out.Reboot.Message = val.(string) 1041 | } 1042 | if val, ok := reboot["startup_disk"]; ok { 1043 | out.Reboot.StartupDisk = val.(string) 1044 | } 1045 | if val, ok := reboot["specify_startup"]; ok { 1046 | out.Reboot.SpecifyStartup = val.(string) 1047 | } 1048 | if val, ok := reboot["no_user_logged_in"]; ok { 1049 | out.Reboot.NoUserLoggedIn = val.(string) 1050 | } 1051 | if val, ok := reboot["user_logged_in"]; ok { 1052 | out.Reboot.UserLoggedIn = val.(string) 1053 | } 1054 | if val, ok := reboot["minutes_until_reboot"]; ok { 1055 | out.Reboot.MinutesUntilReboot = val.(int) 1056 | } 1057 | if val, ok := reboot["start_reboot_timer_immediately"]; ok { 1058 | out.Reboot.StartRebootTimerImmediately = val.(bool) 1059 | } 1060 | if val, ok := reboot["file_vault_2_reboot"]; ok { 1061 | out.Reboot.FileVault2Reboot = val.(bool) 1062 | } 1063 | } 1064 | 1065 | // Maintenance 1066 | if m, ok := d.GetOk("maintenance"); ok { 1067 | v := m.(*schema.Set).List() 1068 | maintenance := v[0].(map[string]interface{}) 1069 | 1070 | if val, ok := maintenance["recon"]; ok { 1071 | out.Maintenance.Recon = val.(bool) 1072 | } 1073 | if val, ok := maintenance["reset_name"]; ok { 1074 | out.Maintenance.ResetName = val.(bool) 1075 | } 1076 | if val, ok := maintenance["install_all_cached_packages"]; ok { 1077 | out.Maintenance.InstallAllCachedPackages = val.(bool) 1078 | } 1079 | if val, ok := maintenance["heal"]; ok { 1080 | out.Maintenance.Heal = val.(bool) 1081 | } 1082 | if val, ok := maintenance["prebindings"]; ok { 1083 | out.Maintenance.Prebindings = val.(bool) 1084 | } 1085 | if val, ok := maintenance["permissions"]; ok { 1086 | out.Maintenance.Permissions = val.(bool) 1087 | } 1088 | if val, ok := maintenance["byhost"]; ok { 1089 | out.Maintenance.Byhost = val.(bool) 1090 | } 1091 | if val, ok := maintenance["system_cache"]; ok { 1092 | out.Maintenance.SystemCache = val.(bool) 1093 | } 1094 | if val, ok := maintenance["user_cache"]; ok { 1095 | out.Maintenance.UserCache = val.(bool) 1096 | } 1097 | if val, ok := maintenance["verify"]; ok { 1098 | out.Maintenance.Verify = val.(bool) 1099 | } 1100 | } 1101 | 1102 | // Files and Processses 1103 | if f, ok := d.GetOk("files_and_processes"); ok { 1104 | v := f.(*schema.Set).List() 1105 | filesAndProcesses := v[0].(map[string]interface{}) 1106 | 1107 | if val, ok := filesAndProcesses["search_by_path"]; ok { 1108 | out.FilesAndProcesses.SearchByPath = val.(string) 1109 | } 1110 | if val, ok := filesAndProcesses["delete_file"]; ok { 1111 | out.FilesAndProcesses.DeleteFile = val.(bool) 1112 | } 1113 | if val, ok := filesAndProcesses["locate_file"]; ok { 1114 | out.FilesAndProcesses.LocateFile = val.(string) 1115 | } 1116 | if val, ok := filesAndProcesses["update_locate_database"]; ok { 1117 | out.FilesAndProcesses.UpdateLocateDatabase = val.(bool) 1118 | } 1119 | if val, ok := filesAndProcesses["spotlight_search"]; ok { 1120 | out.FilesAndProcesses.SpotlightSearch = val.(string) 1121 | } 1122 | if val, ok := filesAndProcesses["search_for_process"]; ok { 1123 | out.FilesAndProcesses.SearchForProcess = val.(string) 1124 | } 1125 | if val, ok := filesAndProcesses["kill_process"]; ok { 1126 | out.FilesAndProcesses.KillProcess = val.(bool) 1127 | } 1128 | if val, ok := filesAndProcesses["run_command"]; ok { 1129 | out.FilesAndProcesses.RunCommand = val.(string) 1130 | } 1131 | } 1132 | 1133 | // User Interaction 1134 | if u, ok := d.GetOk("user_interaction"); ok { 1135 | v := u.(*schema.Set).List() 1136 | userInteraction := v[0].(map[string]interface{}) 1137 | 1138 | if val, ok := userInteraction["message_start"]; ok { 1139 | out.UserInteraction.MessageStart = val.(string) 1140 | } 1141 | if val, ok := userInteraction["allow_users_to_defer"]; ok { 1142 | out.UserInteraction.AllowUsersToDefer = val.(bool) 1143 | } 1144 | if val, ok := userInteraction["allow_deferral_until_utc"]; ok { 1145 | out.UserInteraction.AllowDeferralUntilUtc = val.(string) 1146 | } 1147 | if val, ok := userInteraction["allow_deferral_minutes"]; ok { 1148 | out.UserInteraction.AllowDeferralMinutes = val.(int) 1149 | } 1150 | if val, ok := userInteraction["message_finish"]; ok { 1151 | out.UserInteraction.MessageFinish = val.(string) 1152 | } 1153 | } 1154 | 1155 | return &out 1156 | } 1157 | 1158 | func resourceJamfPolicyCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 1159 | c := m.(*jamf.Client) 1160 | 1161 | b := buildJamfPolicyStruct(d) 1162 | id, err := c.CreatePolicy(b) 1163 | if err != nil { 1164 | return diag.FromErr(err) 1165 | } 1166 | 1167 | d.SetId(strconv.Itoa(id)) 1168 | 1169 | return resourceJamfPolicyRead(ctx, d, m) 1170 | } 1171 | 1172 | func resourceJamfPolicyRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 1173 | var diags diag.Diagnostics 1174 | c := m.(*jamf.Client) 1175 | 1176 | id, err := strconv.Atoi(d.Id()) 1177 | if err != nil { 1178 | return diag.FromErr(err) 1179 | } 1180 | resp, err := c.GetPolicy(id) 1181 | if err != nil { 1182 | if jamfErr, ok := err.(jamf.Error); ok && jamfErr.StatusCode() == 404 { 1183 | d.SetId("") 1184 | } else { 1185 | return diag.FromErr(err) 1186 | } 1187 | } else { 1188 | deconstructJamfPolicyStruct(d, resp) 1189 | } 1190 | 1191 | return diags 1192 | } 1193 | 1194 | func resourceJamfPolicyUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 1195 | c := m.(*jamf.Client) 1196 | 1197 | b := buildJamfPolicyStruct(d) 1198 | 1199 | if _, err := c.UpdatePolicy(b); err != nil { 1200 | return diag.FromErr(err) 1201 | } 1202 | 1203 | return resourceJamfPolicyRead(ctx, d, m) 1204 | } 1205 | 1206 | func resourceJamfPolicyDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { 1207 | var diags diag.Diagnostics 1208 | c := m.(*jamf.Client) 1209 | id, err := strconv.Atoi(d.Id()) 1210 | if err != nil { 1211 | return diag.FromErr(err) 1212 | } 1213 | 1214 | if _, err := c.DeletePolicy(id); err != nil { 1215 | return diag.FromErr(err) 1216 | } 1217 | 1218 | d.SetId("") 1219 | 1220 | return diags 1221 | } 1222 | 1223 | func importJamfPolicyState(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { 1224 | c := m.(*jamf.Client) 1225 | d.SetId(d.Id()) 1226 | id, err := strconv.Atoi(d.Id()) 1227 | if err != nil { 1228 | return nil, err 1229 | } 1230 | resp, err := c.GetPolicy(id) 1231 | if err != nil { 1232 | return nil, fmt.Errorf("cannot get Computer Group data") 1233 | } 1234 | 1235 | deconstructJamfPolicyStruct(d, resp) 1236 | 1237 | return []*schema.ResourceData{d}, nil 1238 | } 1239 | --------------------------------------------------------------------------------