├── ISSUES.md ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── question.md │ ├── feature-request.md │ └── bug-report.md ├── workflows │ ├── release.yml │ └── test-acc.yml └── CODE_OF_CONDUCT.md ├── .gitignore ├── examples ├── data-sources │ ├── freeipa_group │ │ └── data-source.tf │ ├── freeipa_host │ │ └── data-source.tf │ ├── freeipa_sudo_rule │ │ └── data-source.tf │ ├── freeipa_hbac_policy │ │ └── data-source.tf │ ├── freeipa_hostgroup │ │ └── data-source.tf │ ├── freeipa_sudo_cmdgroup │ │ └── data-source.tf │ ├── freeipa_dns_zone │ │ └── data-source.tf │ ├── freeipa_user │ │ └── data-source.tf │ └── freeipa_dns_record │ │ └── data-source.tf ├── resources │ ├── freeipa_sudo_cmd │ │ ├── resource.tf │ │ └── import.sh │ ├── freeipa_hostgroup │ │ ├── resource.tf │ │ └── import.sh │ ├── freeipa_sudo_rule_option │ │ └── resource.tf │ ├── freeipa_sudo_rule │ │ ├── resource.tf │ │ └── import.sh │ ├── freeipa_dns_zone │ │ ├── resource.tf │ │ └── import.sh │ ├── freeipa_sudo_cmdgroup │ │ ├── resource.tf │ │ └── import.sh │ ├── freeipa_hbac_policy │ │ ├── resource.tf │ │ └── import.sh │ ├── freeipa_host │ │ ├── resource.tf │ │ └── import.sh │ ├── freeipa_automemberadd │ │ └── resource.tf │ ├── freeipa_sudo_rule_runasuser_membership │ │ └── resource.tf │ ├── freeipa_sudo_rule_runasgroup_membership │ │ └── resource.tf │ ├── freeipa_group │ │ ├── resource.tf │ │ └── import.sh │ ├── freeipa_host_hostgroup_membership │ │ └── resource.tf │ ├── freeipa_user │ │ ├── resource.tf │ │ └── import.sh │ ├── freeipa_automemberadd_condition │ │ └── resource.tf │ ├── freeipa_dns_record │ │ ├── resource.tf │ │ └── import.sh │ ├── freeipa_sudo_rule_user_membership │ │ └── resource.tf │ ├── freeipa_user_group_membership │ │ └── resource.tf │ ├── freeipa_sudo_rule_host_membership │ │ └── resource.tf │ ├── freeipa_sudo_rule_allowcmd_membership │ │ └── resource.tf │ ├── freeipa_sudo_cmdgroup_membership │ │ └── resource.tf │ ├── freeipa_sudo_rule_denycmd_membership │ │ └── resource.tf │ ├── freeipa_hbac_policy_host_membership │ │ └── resource.tf │ ├── freeipa_hbac_policy_service_membership │ │ └── resource.tf │ └── freeipa_hbac_policy_user_membership │ │ └── resource.tf └── provider │ └── provider.tf ├── terraform-registry-manifest.json ├── tools ├── tools.go └── go.mod ├── GNUmakefile ├── templates ├── data-sources.md.tmpl ├── resources.md.tmpl └── index.md.tmpl ├── docs ├── resources │ ├── sudo_rule_option.md │ ├── hostgroup.md │ ├── sudo_cmd.md │ ├── automemberadd.md │ ├── sudo_cmdgroup.md │ ├── hbac_policy.md │ ├── automemberadd_condition.md │ ├── sudo_rule.md │ ├── sudo_rule_runasuser_membership.md │ ├── sudo_rule_runasgroup_membership.md │ ├── host_hostgroup_membership.md │ ├── sudo_rule_user_membership.md │ ├── sudo_cmdgroup_membership.md │ ├── sudo_rule_host_membership.md │ ├── sudo_rule_allowcmd_membership.md │ ├── sudo_rule_denycmd_membership.md │ ├── hbac_policy_host_membership.md │ ├── hbac_policy_user_membership.md │ ├── hbac_policy_service_membership.md │ ├── user_group_membership.md │ ├── dns_record.md │ ├── host.md │ ├── group.md │ ├── dns_zone.md │ └── user.md ├── data-sources │ ├── dns_record.md │ ├── sudo_cmdgroup.md │ ├── hbac_policy.md │ ├── hostgroup.md │ ├── group.md │ ├── sudo_rule.md │ ├── dns_zone.md │ ├── host.md │ └── user.md └── index.md ├── OUR_CONTRIBUTORS.md ├── freeipa ├── utils.go ├── provider_test.go ├── sudo_cmd_test.go ├── sudo_cmdgroup_test.go ├── hostgroup_test.go ├── dns_record_data_source.go ├── dns_zone_test.go ├── sudo_rule_option_test.go ├── sudo_cmdgroup_data_source.go ├── dns_record_test.go ├── sudo_rule_test.go ├── hbac_policy_service_membership_test.go ├── hbac_policy_data_source.go ├── sudo_rule_option_resource.go └── hbac_policy_test.go ├── README.md ├── .goreleaser.yml ├── main.go ├── docker_compose └── docker-compose.yml ├── go.mod ├── _about └── CONTRIBUTING.md └── resource.template /ISSUES.md: -------------------------------------------------------------------------------- 1 | error returned when enabling/disabling a zone but it succeeds -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [RomanButsiy] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | terraform-provider-freeipa-2 2 | terraform-provider-freeipa 3 | .env 4 | test/ 5 | test-migv4/ -------------------------------------------------------------------------------- /examples/data-sources/freeipa_group/data-source.tf: -------------------------------------------------------------------------------- 1 | data "freeipa_group" "group-0" { 2 | name = "test-group" 3 | } -------------------------------------------------------------------------------- /examples/data-sources/freeipa_host/data-source.tf: -------------------------------------------------------------------------------- 1 | data "freeipa_host" "host-0" { 2 | name = "testhost.example.lan" 3 | } -------------------------------------------------------------------------------- /examples/data-sources/freeipa_sudo_rule/data-source.tf: -------------------------------------------------------------------------------- 1 | data "freeipa_sudo_rule" "operators" { 2 | name = "operators" 3 | } -------------------------------------------------------------------------------- /examples/data-sources/freeipa_hbac_policy/data-source.tf: -------------------------------------------------------------------------------- 1 | data "freeipa_hbac_policy" "myservers" { 2 | name = "myservers" 3 | } -------------------------------------------------------------------------------- /terraform-registry-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "metadata": { 4 | "protocol_versions": ["6.0"] 5 | } 6 | } -------------------------------------------------------------------------------- /examples/data-sources/freeipa_hostgroup/data-source.tf: -------------------------------------------------------------------------------- 1 | data "freeipa_hostgroup" "hostgroup-0" { 2 | name = "testhostgroup" 3 | } -------------------------------------------------------------------------------- /examples/data-sources/freeipa_sudo_cmdgroup/data-source.tf: -------------------------------------------------------------------------------- 1 | data "freeipa_sudo_cmdgroup" "terminals" { 2 | name = "terminals" 3 | } -------------------------------------------------------------------------------- /examples/resources/freeipa_sudo_cmd/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_sudo_cmd" "bash" { 2 | name = "/bin/bash" 3 | description = "The bash terminal" 4 | } 5 | -------------------------------------------------------------------------------- /examples/resources/freeipa_hostgroup/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_hostgroup" "hostgroup-1" { 2 | name = "hostgroup-1" 3 | description = "FreeIPA hostgroup" 4 | } 5 | -------------------------------------------------------------------------------- /examples/resources/freeipa_sudo_rule_option/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_sudo_rule_option" "option-0" { 2 | name = "sudo-rule-test" 3 | option = "!authenticate" 4 | } 5 | 6 | -------------------------------------------------------------------------------- /examples/resources/freeipa_sudo_rule/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_sudo_rule" "sysadmins" { 2 | name = "sysadmins" 3 | description = "Sysadmins have all permissions" 4 | } 5 | -------------------------------------------------------------------------------- /examples/resources/freeipa_dns_zone/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_dns_zone" "dns_zone-2" { 2 | zone_name = "test.roman.com.ua" 3 | skip_overlap_check = true 4 | disable_zone = false 5 | } 6 | -------------------------------------------------------------------------------- /examples/resources/freeipa_sudo_rule/import.sh: -------------------------------------------------------------------------------- 1 | import { 2 | to = freeipa_sudo_rule.testsudorule 3 | id = "testsudorule" 4 | } 5 | 6 | resource "freeipa_sudo_rule" "testsudorule" { 7 | name = "testsudorule" 8 | } -------------------------------------------------------------------------------- /examples/resources/freeipa_sudo_cmdgroup/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_sudo_cmdgroup" "service_management" { 2 | name = "service-management" 3 | description = "Service management related sudo commands" 4 | } 5 | -------------------------------------------------------------------------------- /examples/data-sources/freeipa_dns_zone/data-source.tf: -------------------------------------------------------------------------------- 1 | data "freeipa_dns_zone" "dns-zone-0" { 2 | zone_name = "test.example.lan." 3 | } 4 | 5 | data "freeipa_dns_zone" "dns-zone-1" { 6 | zone_name = "23.168.192.in-addr.arpa." 7 | } 8 | -------------------------------------------------------------------------------- /examples/resources/freeipa_hbac_policy/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_hbac_policy" "hbac-0" { 2 | name = "test-hbac" 3 | description = "Test HBAC policy" 4 | enabled = true 5 | hostcategory = "all" 6 | servicecategory = "all" 7 | } 8 | -------------------------------------------------------------------------------- /examples/resources/freeipa_sudo_cmd/import.sh: -------------------------------------------------------------------------------- 1 | # The import id must be exactly the same as the sudo command. 2 | 3 | import { 4 | to = freeipa_sudo_cmd.testsudocmd 5 | id = "/bin/bash" 6 | } 7 | 8 | resource "freeipa_sudo_cmd" "testsudocmd" { 9 | name = "/bin/bash" 10 | } -------------------------------------------------------------------------------- /examples/data-sources/freeipa_user/data-source.tf: -------------------------------------------------------------------------------- 1 | # Lookup an active user 2 | 3 | data "freeipa_user" "user-0" { 4 | name = "test-user" 5 | } 6 | 7 | # Lookup an staged user 8 | 9 | data "freeipa_user" "user-0" { 10 | name = "test-user" 11 | state = "staged" 12 | } 13 | -------------------------------------------------------------------------------- /examples/resources/freeipa_host/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_host" "host-1" { 2 | name = "host-1.example.test" 3 | ip_address = "192.168.1.65" 4 | description = "FreeIPA client in example.test domain" 5 | mac_addresses = ["00:00:00:AA:AA:AA", "00:00:00:BB:BB:BB"] 6 | } 7 | -------------------------------------------------------------------------------- /examples/resources/freeipa_hbac_policy/import.sh: -------------------------------------------------------------------------------- 1 | # The import id must be exactly the same as the name of the HBAC policy. 2 | 3 | import { 4 | to = freeipa_hbac_policy.testhbac 5 | id = "testhbac" 6 | } 7 | 8 | resource "freeipa_hbac_policy" "testhbac" { 9 | name = "testhbac" 10 | } -------------------------------------------------------------------------------- /tools/tools.go: -------------------------------------------------------------------------------- 1 | // Authors: 2 | // Roman Butsiy 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-only 5 | //go:build tools 6 | 7 | package tools 8 | 9 | import ( 10 | // document generation 11 | _ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs" 12 | ) 13 | -------------------------------------------------------------------------------- /examples/resources/freeipa_hostgroup/import.sh: -------------------------------------------------------------------------------- 1 | # The import id must be exactly the same as the name of the host group. 2 | 3 | import { 4 | to = freeipa_hostgroup.testhostgroup 5 | id = "testhostgroup" 6 | } 7 | 8 | resource "freeipa_hostgroup" "testhostgroup" { 9 | name = "testhostgroup" 10 | } -------------------------------------------------------------------------------- /examples/data-sources/freeipa_dns_record/data-source.tf: -------------------------------------------------------------------------------- 1 | data "freeipa_dns_record" "dns-record-0" { 2 | record_name = "test-record-A" 3 | zone_name = "test.example.lan." 4 | } 5 | 6 | data "freeipa_dns_record" "dns-zone-1" { 7 | record_name = "10" 8 | zone_name = "23.168.192.in-addr.arpa." 9 | } 10 | -------------------------------------------------------------------------------- /examples/resources/freeipa_automemberadd/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_hostgroup" "hostgroup" { 2 | name = "my-hostgroup" 3 | description = "my-hostgroup desc" 4 | } 5 | 6 | resource "freeipa_automemberadd" "automember" { 7 | name = freeipa_hostgroup.hostgroup.name 8 | type = "hostgroup" 9 | } 10 | -------------------------------------------------------------------------------- /examples/resources/freeipa_host/import.sh: -------------------------------------------------------------------------------- 1 | # The import id must be exactly the same as the name of the host, which must be the fqdn of the host. 2 | 3 | import { 4 | to = freeipa_host.testhost 5 | id = "testhost.ipatest.lan" 6 | } 7 | 8 | resource "freeipa_host" "testhost" { 9 | name = "testhost.ipatest.lan" 10 | } -------------------------------------------------------------------------------- /examples/resources/freeipa_sudo_cmdgroup/import.sh: -------------------------------------------------------------------------------- 1 | # The import id must be exactly the same as the name of the sudo command group. 2 | 3 | import { 4 | to = freeipa_sudo_cmdgroup.testsudocmdgrp 5 | id = "testsudocmdgrp" 6 | } 7 | 8 | 9 | resource "freeipa_sudo_cmdgroup" "testsudocmdgrp" { 10 | name = "testsudocmdgrp" 11 | } -------------------------------------------------------------------------------- /examples/provider/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | freeipa = { 4 | version = "5.1.0" 5 | source = "rework-space-com/freeipa" 6 | } 7 | } 8 | } 9 | 10 | provider "freeipa" { 11 | host = "ipa.example.test" 12 | username = "admin" 13 | password = "123456789" 14 | insecure = true 15 | } 16 | -------------------------------------------------------------------------------- /examples/resources/freeipa_sudo_rule_runasuser_membership/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_sudo_rule_runasuser_membership" "user-0" { 2 | name = "sudo-rule-test" 3 | runasuser = "user01" 4 | } 5 | 6 | resource "freeipa_sudo_rule_runasuser_membership" "users-0" { 7 | name = "sudo-rule-test" 8 | runasusers = ["user01", "user02"] 9 | identifier = "users-0" 10 | } 11 | -------------------------------------------------------------------------------- /examples/resources/freeipa_sudo_rule_runasgroup_membership/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_sudo_rule_runasgroup_membership" "group-0" { 2 | name = "sudo-rule-test" 3 | runasgroup = "group01" 4 | } 5 | 6 | resource "freeipa_sudo_rule_runasgroup_membership" "groups-0" { 7 | name = "sudo-rule-test" 8 | runasgroups = ["group01", "group02"] 9 | identifier = "groups-0" 10 | } -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | default: install 2 | 3 | install: 4 | go build -o ~/go/bin/terraform-provider-freeipa 5 | 6 | build: 7 | go build -o $(shell pwd)/terraform-provider-freeipa 8 | 9 | testacc: 10 | TF_ACC=1 go test ./... -v $(TESTARGS) -timeout 120m 11 | 12 | doc: 13 | go generate ./... 14 | 15 | fmt: 16 | go fmt ./... 17 | 18 | deps: 19 | go mod tidy 20 | 21 | .PHONY: install build testacc doc fmt deps -------------------------------------------------------------------------------- /templates/data-sources.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "{{ .Name }} {{ .Type }} - {{ .ProviderShortName }}" 3 | description: |- 4 | {{ .Description | plainmarkdown | trimspace | prefixlines " " }} 5 | --- 6 | 7 | # {{ .Name }} ({{ .Type }}) 8 | 9 | {{ .Description | trimspace }} 10 | 11 | {{if .HasExample}} 12 | ## Example Usage 13 | 14 | {{tffile .ExampleFile}} 15 | {{end}} 16 | 17 | {{ .SchemaMarkdown | trimspace }} 18 | -------------------------------------------------------------------------------- /examples/resources/freeipa_group/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_group" "group-posix" { 2 | name = "test-group" 3 | description = "Test group" 4 | gid_number = "12345789" 5 | } 6 | 7 | resource "freeipa_group" "group-nonposix" { 8 | name = "test-group" 9 | description = "Test group" 10 | nonposix = true 11 | } 12 | 13 | resource "freeipa_group" "group-external" { 14 | name = "test-group" 15 | description = "Test group" 16 | external = true 17 | } -------------------------------------------------------------------------------- /templates/resources.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "{{ .Name }} {{ .Type }} - {{ .ProviderShortName }}" 3 | description: |- 4 | {{ .Description | plainmarkdown | trimspace | prefixlines " " }} 5 | --- 6 | 7 | # {{ .Name }} ({{ .Type }}) 8 | 9 | {{ .Description | trimspace }} 10 | 11 | {{if .HasExample}} 12 | ## Example Usage 13 | 14 | {{tffile .ExampleFile}} 15 | {{end}} 16 | 17 | {{if .HasImport}} 18 | ## Import Usage 19 | 20 | {{tffile .ImportFile}} 21 | {{end}} 22 | 23 | {{ .SchemaMarkdown | trimspace }} 24 | -------------------------------------------------------------------------------- /templates/index.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Provider: {{ .ProviderShortName | upper }}" 3 | description: |- 4 | {{ .Description | plainmarkdown | trimspace | prefixlines " " }} 5 | --- 6 | 7 | # {{ .ProviderShortName | upper }} Provider 8 | 9 | {{ .Description | trimspace }} 10 | 11 | The provider needs to be configured with the proper endpoints and credentials before it can be used. 12 | 13 | ## Example Usage 14 | 15 | {{if .HasExample}}{{tffile .ExampleFile}}{{end}} 16 | 17 | {{ .SchemaMarkdown | trimspace }} 18 | -------------------------------------------------------------------------------- /examples/resources/freeipa_host_hostgroup_membership/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_host_hostgroup_membership" "test-0" { 2 | name = "test-hostgroup-2" 3 | host = "test.example.test" 4 | } 5 | 6 | resource "freeipa_host_hostgroup_membership" "test-1" { 7 | name = "test-hostgroup-2" 8 | hostgroup = "test-hostgroup" 9 | } 10 | 11 | resource "freeipa_host_hostgroup_membership" "test-2" { 12 | name = "test-hostgroup-2" 13 | hosts = ["host1", "host2"] 14 | hostgroups = ["test-hostgroup", "test-hostgroup2"] 15 | identifier = "my_unique_identifier" 16 | } 17 | -------------------------------------------------------------------------------- /examples/resources/freeipa_user/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_user" "user-1" { 2 | first_name = "Roman" 3 | last_name = "Roman" 4 | name = "roman" 5 | telephone_numbers = ["+380982555429", "2-10-11"] 6 | email_address = ["roman@example.com"] 7 | } 8 | 9 | resource "freeipa_user" "user-2" { 10 | first_name = "Roman" 11 | last_name = "Roman" 12 | name = "roman" 13 | state = "staged" 14 | telephone_numbers = ["+380982555429", "2-10-11"] 15 | email_address = ["roman@example.com"] 16 | } 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F914 Question" 3 | about: "If you need help figuring something out" 4 | title: '' 5 | labels: question 6 | 7 | --- 8 | 9 | 12 | 13 | ## Terraform version, provider version and FreeIPA version 14 | ``` 15 | Terraform version: 16 | FreeIPA Provider version: 17 | FreeIPA version: 18 | ``` 19 | ## Terraform configuration 20 | ```hcl 21 | Enter your configuration here. 22 | ``` 23 | 24 | ## Question 25 | ``` 26 | Enter your question here. 27 | ``` -------------------------------------------------------------------------------- /examples/resources/freeipa_automemberadd_condition/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_hostgroup" "hostgroup" { 2 | name = "my-hostgroup" 3 | description = "my-hostgroup desc" 4 | } 5 | 6 | resource "freeipa_automemberadd" "automember" { 7 | name = freeipa_hostgroup.hostgroup.name 8 | type = "hostgroup" 9 | } 10 | 11 | resource "freeipa_automemberadd_condition" "automembercondition" { 12 | name = freeipa_automemberadd.automember.name 13 | type = "hostgroup" 14 | key = "fqdn" 15 | inclusiveregex = ["\\.my\\.first\\.net$", "\\.my\\.second\\.net$"] 16 | } 17 | -------------------------------------------------------------------------------- /examples/resources/freeipa_dns_record/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_dns_zone" "dns_zone-2" { 2 | zone_name = "test.roman.com.ua." 3 | skip_overlap_check = true 4 | } 5 | 6 | resource "freeipa_dns_record" "record-8" { 7 | zone_name = resource.freeipa_dns_zone.dns_zone-2.id 8 | name = "test-record" 9 | records = ["192.168.10.10", "192.168.10.11"] 10 | type = "A" 11 | } 12 | 13 | resource "freeipa_dns_record" "record-7" { 14 | zone_name = "record.com.ua." 15 | name = "test-record" 16 | records = ["2 1 84DE37B22918F76ED66910B47EB440B0A35F4A56"] 17 | type = "SSHFP" 18 | } 19 | -------------------------------------------------------------------------------- /examples/resources/freeipa_sudo_rule_user_membership/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_sudo_rule_user_membership" "user-0" { 2 | name = "sudo-rule-test" 3 | user = "user01" 4 | } 5 | 6 | resource "freeipa_sudo_rule_user_membership" "users-1" { 7 | name = "sudo-rule-test" 8 | users = ["user01"] 9 | identifier = "users-1" 10 | } 11 | 12 | resource "freeipa_sudo_rule_user_membership" "group-3" { 13 | name = "sudo-rule-test" 14 | group = "test-group-0" 15 | } 16 | 17 | resource "freeipa_sudo_rule_user_membership" "groups-3" { 18 | name = "sudo-rule-test" 19 | groups = ["test-group-0"] 20 | identifier = "groups-3" 21 | } 22 | -------------------------------------------------------------------------------- /examples/resources/freeipa_user_group_membership/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_user_group_membership" "test-0" { 2 | name = "test-group-2" 3 | user = "roman" 4 | } 5 | 6 | resource "freeipa_user_group_membership" "test-1" { 7 | name = "test-group-2" 8 | group = "test-group" 9 | } 10 | 11 | resource "freeipa_user_group_membership" "test-2" { 12 | name = "test-group-2" 13 | external_member = "domain users@adtest.lan" 14 | } 15 | 16 | resource "freeipa_user_group_membership" "test-3" { 17 | name = "test-group-3" 18 | users = ["user1", "user2"] 19 | groups = ["group1", "group2"] 20 | identifier = "my_unique_identifier" 21 | } 22 | -------------------------------------------------------------------------------- /examples/resources/freeipa_sudo_rule_host_membership/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_sudo_rule_host_membership" "host-0" { 2 | name = "sudo-rule-test" 3 | host = "test.example.test" 4 | } 5 | 6 | resource "freeipa_sudo_rule_host_membership" "hosts-0" { 7 | name = "sudo-rule-test" 8 | hosts = ["test.example.test"] 9 | identifier = "hosts-0" 10 | } 11 | 12 | resource "freeipa_sudo_rule_host_membership" "hostgroup-3" { 13 | name = "sudo-rule-test" 14 | hostgroup = "test-hostgroup" 15 | } 16 | 17 | resource "freeipa_sudo_rule_host_membership" "hostgroups-3" { 18 | name = "sudo-rule-test" 19 | hostgroups = ["test-hostgroup"] 20 | identifier = "hostgroups-3" 21 | } -------------------------------------------------------------------------------- /docs/resources/sudo_rule_option.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_sudo_rule_option Resource - freeipa" 3 | description: |- 4 | FreeIPA Sudo rule option resource 5 | --- 6 | 7 | # freeipa_sudo_rule_option (Resource) 8 | 9 | FreeIPA Sudo rule option resource 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | resource "freeipa_sudo_rule_option" "option-0" { 16 | name = "sudo-rule-test" 17 | option = "!authenticate" 18 | } 19 | ``` 20 | 21 | 22 | 23 | 24 | 25 | ## Schema 26 | 27 | ### Required 28 | 29 | - `name` (String) Sudo rule name 30 | - `option` (String) Sudo option to add to the sudo rule. 31 | 32 | ### Read-Only 33 | 34 | - `id` (String) ID of the resource 35 | -------------------------------------------------------------------------------- /examples/resources/freeipa_dns_zone/import.sh: -------------------------------------------------------------------------------- 1 | # forward zone 2 | # The import id attribute must be the undotted fqdn of the zone to import 3 | 4 | import { 5 | to = freeipa_dns_zone.testzone 6 | id = "testimport.ipatest.lan" 7 | } 8 | 9 | resource "freeipa_dns_zone" "testzone" { 10 | zone_name = "testimport.ipatest.lan" 11 | } 12 | 13 | # reverse zone 14 | # The import id attribute must be the undotted fqdn of the zone to import 15 | 16 | import { 17 | to = freeipa_dns_zone.reversetestzone 18 | id = "2.27.172.in-addr.arpa" 19 | } 20 | 21 | resource "freeipa_dns_zone" "reversetestzone" { 22 | zone_name = "2.27.172.in-addr.arpa" 23 | } 24 | 25 | # note that the is_reverse_zone must not be defined, this is only useful for creation -------------------------------------------------------------------------------- /examples/resources/freeipa_sudo_rule_allowcmd_membership/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_sudo_rule_allowcmd_membership" "allowed_cmd" { 2 | name = "sudo-rule-editors" 3 | sudocmd = "/bin/bash" 4 | } 5 | 6 | resource "freeipa_sudo_rule_allowcmd_membership" "allowed_cmd" { 7 | name = "sudo-rule-editors" 8 | sudocmds = ["/bin/bash"] 9 | identifier = "allowed_bash" 10 | } 11 | 12 | resource "freeipa_sudo_rule_allowcmd_membership" "allowed_cmdgrp" { 13 | name = "sudo-rule-editors" 14 | sudocmd_group = "allowed-terminals" 15 | } 16 | 17 | resource "freeipa_sudo_rule_allowcmd_membership" "allowed_cmdgrp" { 18 | name = "sudo-rule-editors" 19 | sudocmd_groups = ["allowed-terminals"] 20 | identifier = "allowed_terminals" 21 | } 22 | 23 | -------------------------------------------------------------------------------- /examples/resources/freeipa_sudo_cmdgroup_membership/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_sudo_cmd" "bash" { 2 | name = "/bin/bash" 3 | description = "The bash shell" 4 | } 5 | 6 | resource "freeipa_sudo_cmd" "fish" { 7 | name = "/bin/fish" 8 | description = "The fish shell" 9 | } 10 | 11 | resource "freeipa_sudo_cmdgroup" "terminals" { 12 | name = "terminals" 13 | description = "The terminals allowed to be sudoed" 14 | } 15 | 16 | resource "freeipa_sudo_cmdgroup_membership" "terminal_bash" { 17 | name = freeipa_sudo_cmdgroup.terminals.id 18 | sudocmd = freeipa_sudo_cmd.bash.id 19 | } 20 | 21 | resource "freeipa_sudo_cmdgroup_membership" "terminal_fish" { 22 | name = freeipa_sudo_cmdgroup.terminals.id 23 | sudocmd = freeipa_sudo_cmd.fish.id 24 | } 25 | -------------------------------------------------------------------------------- /examples/resources/freeipa_sudo_rule_denycmd_membership/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_sudo_rule_denycmd_membership" "denied_cmd" { 2 | name = "sudo-rule-restricted" 3 | sudocmd = "/usr/bin/systemctl" 4 | } 5 | 6 | resource "freeipa_sudo_rule_denycmd_membership" "denied_cmd" { 7 | name = "sudo-rule-restricted" 8 | sudocmds = ["/usr/bin/systemctl"] 9 | identifier = "denied_systemctl" 10 | } 11 | 12 | resource "freeipa_sudo_rule_denycmd_membership" "denied_cmdgrp" { 13 | name = "sudo-rule-restricted" 14 | sudocmd_group = "service-management" 15 | } 16 | 17 | resource "freeipa_sudo_rule_denycmd_membership" "denied_cmdgrp" { 18 | name = "sudo-rule-restricted" 19 | sudocmd_groups = ["service-management"] 20 | identifier = "denied_services" 21 | } -------------------------------------------------------------------------------- /docs/data-sources/dns_record.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_dns_record Data Source - freeipa" 3 | description: |- 4 | FreeIPA DNS Record data source 5 | --- 6 | 7 | # freeipa_dns_record (Data Source) 8 | 9 | FreeIPA DNS Record data source 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | data "freeipa_dns_record" "dns-record-0" { 16 | record_name = "test-record-A" 17 | zone_name = "test.example.lan." 18 | } 19 | 20 | data "freeipa_dns_record" "dns-zone-1" { 21 | record_name = "10" 22 | zone_name = "23.168.192.in-addr.arpa." 23 | } 24 | ``` 25 | 26 | 27 | 28 | ## Schema 29 | 30 | ### Required 31 | 32 | - `record_name` (String) Record name 33 | - `zone_name` (String) Zone name (FQDN) 34 | 35 | ### Read-Only 36 | 37 | - `id` (String) ID of the resource 38 | -------------------------------------------------------------------------------- /docs/data-sources/sudo_cmdgroup.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_sudo_cmdgroup Data Source - freeipa" 3 | description: |- 4 | FreeIPA Sudo command group data source 5 | --- 6 | 7 | # freeipa_sudo_cmdgroup (Data Source) 8 | 9 | FreeIPA Sudo command group data source 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | data "freeipa_sudo_cmdgroup" "terminals" { 16 | name = "terminals" 17 | } 18 | ``` 19 | 20 | 21 | 22 | ## Schema 23 | 24 | ### Required 25 | 26 | - `name` (String) Name of the sudo command group 27 | 28 | ### Read-Only 29 | 30 | - `description` (String) Description of the sudo command group 31 | - `id` (String) ID of the resource in the terraform state 32 | - `member_sudocmd` (List of String) List of sudo commands that are member of the sudo command group 33 | -------------------------------------------------------------------------------- /examples/resources/freeipa_hbac_policy_host_membership/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_hbac_policy" "hbac-0" { 2 | name = "test-hbac" 3 | description = "Test HBAC policy" 4 | enabled = true 5 | } 6 | 7 | resource "freeipa_hbac_policy_host_membership" "hbac-host-1" { 8 | name = "test-hbac" 9 | host = "ipaclient1.ipatest.lan" 10 | } 11 | 12 | resource "freeipa_hbac_policy_host_membership" "hbac-hosts-1" { 13 | name = "test-hbac" 14 | hosts = ["ipaclient1.ipatest.lan", "ipaclient2.ipatest.lan"] 15 | identifier = "hbac-hosts-1" 16 | } 17 | 18 | resource "freeipa_hbac_policy_host_membership" "hostgroup-3" { 19 | name = "test-hbac" 20 | hostgroup = "test-hostgroup" 21 | } 22 | 23 | resource "freeipa_hbac_policy_host_membership" "hostgroups-3" { 24 | name = "test-hbac" 25 | hostgroups = ["test-hostgroup", "test-hostgroup-2"] 26 | identifier = "hostgroups-3" 27 | } -------------------------------------------------------------------------------- /examples/resources/freeipa_hbac_policy_service_membership/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_hbac_policy" "hbac-0" { 2 | name = "test-hbac" 3 | description = "Test HBAC policy" 4 | enabled = true 5 | hostcategory = "all" 6 | servicecategory = "all" 7 | } 8 | 9 | resource "freeipa_hbac_policy_service_membership" "hbac-svc-1" { 10 | name = "test-hbac" 11 | service = "sshd" 12 | } 13 | 14 | resource "freeipa_hbac_policy_service_membership" "hbac-svc-2" { 15 | name = "test-hbac" 16 | services = ["sshd"] 17 | identifier = "hbac-svc-2" 18 | } 19 | 20 | resource "freeipa_hbac_policy_service_membership" "hbac-svcgrp-1" { 21 | name = "test-hbac" 22 | servicegroup = "Sudo" 23 | } 24 | 25 | resource "freeipa_hbac_policy_service_membership" "hbac-svcgrp-2" { 26 | name = "test-hbac" 27 | servicegroups = ["Sudo", "ftp"] 28 | identifier = "hbac-svcgrp-2" 29 | } 30 | -------------------------------------------------------------------------------- /examples/resources/freeipa_hbac_policy_user_membership/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_hbac_policy" "hbac-0" { 2 | name = "test-hbac" 3 | description = "Test HBAC policy" 4 | enabled = true 5 | hostcategory = "all" 6 | servicecategory = "all" 7 | } 8 | 9 | resource "freeipa_hbac_policy_user_membership" "hbac-user-1" { 10 | name = "test-hbac" 11 | user = "user-1" 12 | } 13 | 14 | resource "freeipa_hbac_policy_user_membership" "hbac-users-1" { 15 | name = "test-hbac" 16 | users = ["user-2", "user-3"] 17 | identifier = "hbac-users-1" 18 | } 19 | 20 | resource "freeipa_hbac_policy_user_membership" "hbac-group-1" { 21 | name = "test-hbac" 22 | group = "usergroup-1" 23 | } 24 | 25 | resource "freeipa_hbac_policy_user_membership" "hbac-groups-1" { 26 | name = "test-hbac" 27 | groups = ["usergroup-2", "usergroup-3"] 28 | identifier = "hbac-groups-1" 29 | } 30 | -------------------------------------------------------------------------------- /OUR_CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | This file lists individuals who have contributed significantly to the development, maintenance, or design of this project. The order is alphabetical by first name. 4 | 5 | If you are a contributor and your name is missing, feel free to submit a pull request. 6 | 7 | ## Core Contributors 8 | 9 | - **Antoine Gatineau** — 10 | - **Benjamin Staffin** — 11 | - **fdugast** — 12 | - **Mixton** — 13 | - **Mykhailo Holubovskyi** — 14 | - **Parsa** — 15 | - **Roman Butsiy** — 16 | - **Vitaly Brevus** — 17 | 18 | --- 19 | 20 | > This project follows open contribution principles. Attribution is important — if you contribute code, documentation, or ideas, your name belongs here. 21 | -------------------------------------------------------------------------------- /docs/resources/hostgroup.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_hostgroup Resource - freeipa" 3 | description: |- 4 | FreeIPA Host Group resource 5 | --- 6 | 7 | # freeipa_hostgroup (Resource) 8 | 9 | FreeIPA Host Group resource 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | resource "freeipa_hostgroup" "hostgroup-1" { 16 | name = "hostgroup-1" 17 | description = "FreeIPA hostgroup" 18 | } 19 | ``` 20 | 21 | 22 | 23 | ## Import Usage 24 | 25 | ```terraform 26 | # The import id must be exactly the same as the name of the host group. 27 | 28 | import { 29 | to = freeipa_hostgroup.testhostgroup 30 | id = "testhostgroup" 31 | } 32 | 33 | resource "freeipa_hostgroup" "testhostgroup" { 34 | name = "testhostgroup" 35 | } 36 | ``` 37 | 38 | 39 | 40 | ## Schema 41 | 42 | ### Required 43 | 44 | - `name` (String) Hostgroup name 45 | 46 | ### Optional 47 | 48 | - `description` (String) Hostgroup Description 49 | 50 | ### Read-Only 51 | 52 | - `id` (String) ID of the resource 53 | -------------------------------------------------------------------------------- /docs/resources/sudo_cmd.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_sudo_cmd Resource - freeipa" 3 | description: |- 4 | FreeIPA Sudo command resource 5 | --- 6 | 7 | # freeipa_sudo_cmd (Resource) 8 | 9 | FreeIPA Sudo command resource 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | resource "freeipa_sudo_cmd" "bash" { 16 | name = "/bin/bash" 17 | description = "The bash terminal" 18 | } 19 | ``` 20 | 21 | 22 | 23 | ## Import Usage 24 | 25 | ```terraform 26 | # The import id must be exactly the same as the sudo command. 27 | 28 | import { 29 | to = freeipa_sudo_cmd.testsudocmd 30 | id = "/bin/bash" 31 | } 32 | 33 | resource "freeipa_sudo_cmd" "testsudocmd" { 34 | name = "/bin/bash" 35 | } 36 | ``` 37 | 38 | 39 | 40 | ## Schema 41 | 42 | ### Required 43 | 44 | - `name` (String) Absolute path of the sudo command (case sensitive) 45 | 46 | ### Optional 47 | 48 | - `description` (String) Sudo command description 49 | 50 | ### Read-Only 51 | 52 | - `id` (String) ID of the resource 53 | -------------------------------------------------------------------------------- /freeipa/utils.go: -------------------------------------------------------------------------------- 1 | // Authors: 2 | // Antoine Gatineau 3 | // Roman Butsiy 4 | // 5 | // SPDX-License-Identifier: GPL-3.0-only 6 | 7 | package freeipa 8 | 9 | import ( 10 | "os" 11 | "strconv" 12 | "strings" 13 | ) 14 | 15 | func getEnvAsBool(name string, defaultVal bool) bool { 16 | valStr := os.Getenv(name) 17 | if val, err := strconv.ParseBool(valStr); err == nil { 18 | return val 19 | } 20 | 21 | return defaultVal 22 | } 23 | 24 | // Some resource names are use construct the resource Id (name/cat/value). If they contain a slash, it messes up with the parsing of resources id. 25 | func encodeSlash(str string) string { 26 | return strings.ReplaceAll(str, string('/'), "%2F") 27 | } 28 | 29 | func decodeSlash(str string) string { 30 | return strings.ReplaceAll(str, "%2F", string('/')) 31 | } 32 | 33 | func isStringListContainsCaseInsensistive(strList *[]string, str *string) bool { 34 | for _, s := range *strList { 35 | if strings.EqualFold(s, *str) { 36 | return true 37 | } 38 | } 39 | return false 40 | } 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Terraform FreeIPA Provider 2 | ============================ 3 | Tested on FreeIPA version 4.12.2 4 | Download provider from [registry.terraform.io](https://registry.terraform.io/providers/rework-space-com/freeipa/latest) 5 | 6 | Requirements 7 | ------------ 8 | 9 | - [Terraform](https://www.terraform.io/downloads.html) 1.0+ 10 | - [Go](https://golang.org/doc/install) 1.22+ (to build the provider plugin) 11 | 12 | Building The Provider 13 | --------------------- 14 | 15 | Clone the repository. Enter the provider directory and build the provider 16 | 17 | ```sh 18 | $ cd terraform-provider-freeipa 19 | $ go build -o ~/go/bin/terraform-provider-freeipa 20 | ``` 21 | ## Contributing to the provider 22 | 23 | To contribute, please read the [contribution guidelines](_about/CONTRIBUTING.md). You may also [report an issue](https://github.com/rework-space-com/terraform-provider-freeipa/issues/new/choose). 24 | 25 | ## 🤝 Contributors 26 | 27 | A full list of contributors is available in [OUR_CONTRIBUTORS.md](./OUR_CONTRIBUTORS.md). 28 | We acknowledge and thank everyone who has contributed to this project. -------------------------------------------------------------------------------- /docs/resources/automemberadd.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_automemberadd Resource - freeipa" 3 | description: |- 4 | FreeIPA Automember resource 5 | --- 6 | 7 | # freeipa_automemberadd (Resource) 8 | 9 | FreeIPA Automember resource 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | resource "freeipa_hostgroup" "hostgroup" { 16 | name = "my-hostgroup" 17 | description = "my-hostgroup desc" 18 | } 19 | 20 | resource "freeipa_automemberadd" "automember" { 21 | name = freeipa_hostgroup.hostgroup.name 22 | type = "hostgroup" 23 | } 24 | ``` 25 | 26 | 27 | 28 | 29 | 30 | ## Schema 31 | 32 | ### Required 33 | 34 | - `name` (String) Automember rule name 35 | - `type` (String) Automember rule type 36 | 37 | ### Optional 38 | 39 | - `addattr` (List of String) Add an attribute/value pair. Format is attr=value. The attribute must be part of the schema. 40 | - `description` (String) Automember rule description 41 | - `setattr` (List of String) Set an attribute to a name/value pair. Format is attr=value. 42 | 43 | ### Read-Only 44 | 45 | - `id` (String) ID of the resource 46 | -------------------------------------------------------------------------------- /examples/resources/freeipa_dns_record/import.sh: -------------------------------------------------------------------------------- 1 | # import id must be of format ;;; 2 | # or without the optional set_identifier : ;; 3 | 4 | import { 5 | to = freeipa_dns_record.txtrecord 6 | id = "_kerberos;testimport.ipatest.lan;TXT;krbtxt" 7 | } 8 | 9 | resource "freeipa_dns_record" "txtrecord" { 10 | name = "_kerberos" 11 | zone_name = "testimport.ipatest.lan" 12 | type = "TXT" 13 | records = ["IPATEST.LAN"] 14 | set_identifier = "krbtxt" 15 | } 16 | 17 | import { 18 | to = freeipa_dns_record.arecord 19 | id = "test;testimport.ipatest.lan;A" 20 | } 21 | 22 | resource "freeipa_dns_record" "arecord" { 23 | name = "test" 24 | zone_name = "testimport.ipatest.lan" 25 | type = "A" 26 | records = ["172.27.2.2"] 27 | } 28 | 29 | import { 30 | to = freeipa_dns_record.ptrrecord 31 | id = "2;2.27.172.in-addr.arpa;PTR" 32 | } 33 | 34 | resource "freeipa_dns_record" "ptrrecord" { 35 | name = "2" 36 | zone_name = "2.27.172.in-addr.arpa" 37 | type = "PTR" 38 | records = ["test.testimport.ipatest.lan."] 39 | } -------------------------------------------------------------------------------- /docs/resources/sudo_cmdgroup.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_sudo_cmdgroup Resource - freeipa" 3 | description: |- 4 | FreeIPA Sudo command group resource 5 | --- 6 | 7 | # freeipa_sudo_cmdgroup (Resource) 8 | 9 | FreeIPA Sudo command group resource 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | resource "freeipa_sudo_cmdgroup" "service_management" { 16 | name = "service-management" 17 | description = "Service management related sudo commands" 18 | } 19 | ``` 20 | 21 | 22 | 23 | ## Import Usage 24 | 25 | ```terraform 26 | # The import id must be exactly the same as the name of the sudo command group. 27 | 28 | import { 29 | to = freeipa_sudo_cmdgroup.testsudocmdgrp 30 | id = "testsudocmdgrp" 31 | } 32 | 33 | 34 | resource "freeipa_sudo_cmdgroup" "testsudocmdgrp" { 35 | name = "testsudocmdgrp" 36 | } 37 | ``` 38 | 39 | 40 | 41 | ## Schema 42 | 43 | ### Required 44 | 45 | - `name` (String) Name of the sudo command group 46 | 47 | ### Optional 48 | 49 | - `description` (String) Sudo command group description 50 | 51 | ### Read-Only 52 | 53 | - `id` (String) ID of the resource 54 | -------------------------------------------------------------------------------- /examples/resources/freeipa_group/import.sh: -------------------------------------------------------------------------------- 1 | # The import id must be exactly the same as the name of the user group. 2 | 3 | # for posix groups, only the name needs to be defined in the resource statement. 4 | import { 5 | to = freeipa_group.group-posix 6 | id = "testposix" 7 | } 8 | 9 | resource "freeipa_group" "group-posix" { 10 | name = "testposix" 11 | } 12 | 13 | # for external groups, the external attribute must also be defined in the resource statement. 14 | import { 15 | to = freeipa_group.group-external 16 | id = "testexternal" 17 | } 18 | 19 | resource "freeipa_group" "group-external" { 20 | name = "testexternal" 21 | external = true 22 | } 23 | 24 | 25 | # for non posix groups, the nonposix attribute must also be defined in the resource statement. 26 | import { 27 | to = freeipa_group.group-nonposix 28 | id = "testnonposix" 29 | } 30 | 31 | resource "freeipa_group" "group-nonposix" { 32 | name = "testnonposix" 33 | nonposix = true 34 | } 35 | 36 | # note that nonposix and external are two mutually exclusive attributes. 37 | # setting the wrong attributes for the group will result in the replacement of the resource (destroy and recreate) 38 | -------------------------------------------------------------------------------- /freeipa/provider_test.go: -------------------------------------------------------------------------------- 1 | // This file was originally inspired by the module structure and design patterns 2 | // used in HashiCorp projects, but all code in this file was written from scratch. 3 | // 4 | // Previously licensed under the MPL-2.0. 5 | // This file is now relicensed under the GNU General Public License v3.0 only, 6 | // as permitted by Section 1.10 of the MPL. 7 | // 8 | // Authors: 9 | // Antoine Gatineau 10 | // Roman Butsiy 11 | // 12 | // SPDX-License-Identifier: GPL-3.0-only 13 | 14 | package freeipa 15 | 16 | import ( 17 | "os" 18 | "testing" 19 | ) 20 | 21 | func testAccPreCheck(t *testing.T) { 22 | // You can add code here to run prior to any test case execution, for example assertions 23 | // about the appropriate environment variables being set are common to see in a pre-check 24 | // function. 25 | 26 | if v := os.Getenv("FREEIPA_HOST"); v == "" { 27 | t.Fatal("FREEIPA_HOST must be set for acceptance tests") 28 | } 29 | if v := os.Getenv("FREEIPA_USERNAME"); v == "" { 30 | t.Fatal("FREEIPA_USERNAME must be set for acceptance tests") 31 | } 32 | if v := os.Getenv("FREEIPA_PASSWORD"); v == "" { 33 | t.Fatal("FREEIPA_PASSWORD must be set for acceptance tests") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /docs/resources/hbac_policy.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_hbac_policy Resource - freeipa" 3 | description: |- 4 | FreeIPA HBAC policy resource 5 | --- 6 | 7 | # freeipa_hbac_policy (Resource) 8 | 9 | FreeIPA HBAC policy resource 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | resource "freeipa_hbac_policy" "hbac-0" { 16 | name = "test-hbac" 17 | description = "Test HBAC policy" 18 | enabled = true 19 | hostcategory = "all" 20 | servicecategory = "all" 21 | } 22 | ``` 23 | 24 | 25 | 26 | ## Import Usage 27 | 28 | ```terraform 29 | # The import id must be exactly the same as the name of the HBAC policy. 30 | 31 | import { 32 | to = freeipa_hbac_policy.testhbac 33 | id = "testhbac" 34 | } 35 | 36 | resource "freeipa_hbac_policy" "testhbac" { 37 | name = "testhbac" 38 | } 39 | ``` 40 | 41 | 42 | 43 | ## Schema 44 | 45 | ### Required 46 | 47 | - `name` (String) Name of the hbac policy 48 | 49 | ### Optional 50 | 51 | - `description` (String) HBAC policy description 52 | - `enabled` (Boolean) Enable this hbac policy 53 | - `hostcategory` (String) Host category the hbac policy is applied to (allowed value: all) 54 | - `servicecategory` (String) Service category the hbac policy is applied to (allowed value: all) 55 | - `usercategory` (String) User category the hbac policy is applied to (allowed value: all) 56 | 57 | ### Read-Only 58 | 59 | - `id` (String) ID of the resource 60 | -------------------------------------------------------------------------------- /docs/resources/automemberadd_condition.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_automemberadd_condition Resource - freeipa" 3 | description: |- 4 | FreeIPA Automember condition resource 5 | --- 6 | 7 | # freeipa_automemberadd_condition (Resource) 8 | 9 | FreeIPA Automember condition resource 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | resource "freeipa_hostgroup" "hostgroup" { 16 | name = "my-hostgroup" 17 | description = "my-hostgroup desc" 18 | } 19 | 20 | resource "freeipa_automemberadd" "automember" { 21 | name = freeipa_hostgroup.hostgroup.name 22 | type = "hostgroup" 23 | } 24 | 25 | resource "freeipa_automemberadd_condition" "automembercondition" { 26 | name = freeipa_automemberadd.automember.name 27 | type = "hostgroup" 28 | key = "fqdn" 29 | inclusiveregex = ["\\.my\\.first\\.net$", "\\.my\\.second\\.net$"] 30 | } 31 | ``` 32 | 33 | 34 | 35 | 36 | 37 | ## Schema 38 | 39 | ### Required 40 | 41 | - `key` (String) Automember rule condition key 42 | - `name` (String) Automember rule condition name 43 | - `type` (String) Automember rule condition type 44 | 45 | ### Optional 46 | 47 | - `description` (String) Automember rule condition description 48 | - `exclusiveregex` (List of String) Regex expression for values that should be excluded. 49 | - `inclusiveregex` (List of String) Regex expression for values that should be included. 50 | 51 | ### Read-Only 52 | 53 | - `id` (String) ID of the resource 54 | -------------------------------------------------------------------------------- /examples/resources/freeipa_user/import.sh: -------------------------------------------------------------------------------- 1 | # The import id of an active must be exactly equal to `username` of the user to import. 2 | 3 | # The associated resource in terraform must include the attributes: 4 | # - `name` 5 | # - `first_name` 6 | # - `last_name` 7 | 8 | import { 9 | to = freeipa_user.testuser 10 | id = "testuser" 11 | } 12 | 13 | resource "freeipa_user" "testuser" { 14 | name = "testuser" 15 | first_name = "Test" 16 | last_name = "User" 17 | } 18 | 19 | # The import id of n staged must be exactly equal to `username;staged` of the user to import. 20 | 21 | # The associated resource in terraform must include the attributes: 22 | # - `name` 23 | # - `first_name` 24 | # - `last_name` 25 | # - `state` 26 | 27 | import { 28 | to = freeipa_user.testuser 29 | id = "testuser;staged" 30 | } 31 | 32 | resource "freeipa_user" "testuser" { 33 | name = "testuser" 34 | first_name = "Test" 35 | last_name = "User" 36 | state = "staged 37 | } 38 | 39 | # The import id of a preserved must be exactly equal to `username;preserved` of the user to import. 40 | 41 | # The associated resource in terraform must include the attributes: 42 | # - `name` 43 | # - `first_name` 44 | # - `last_name` 45 | # - `state` 46 | 47 | import { 48 | to = freeipa_user.testuser 49 | id = "testuser;preserved" 50 | } 51 | 52 | resource "freeipa_user" "testuser" { 53 | name = "testuser" 54 | first_name = "Test" 55 | last_name = "User" 56 | state = "preserved" 57 | } -------------------------------------------------------------------------------- /docs/data-sources/hbac_policy.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_hbac_policy Data Source - freeipa" 3 | description: |- 4 | FreeIPA User hbac policy data source 5 | --- 6 | 7 | # freeipa_hbac_policy (Data Source) 8 | 9 | FreeIPA User hbac policy data source 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | data "freeipa_hbac_policy" "myservers" { 16 | name = "myservers" 17 | } 18 | ``` 19 | 20 | 21 | 22 | ## Schema 23 | 24 | ### Required 25 | 26 | - `name` (String) Name of the hbac policy 27 | 28 | ### Read-Only 29 | 30 | - `description` (String) Description of the hbac policy 31 | - `enabled` (Boolean) Enable this hbac policy 32 | - `hostcategory` (String) Host category the hbac policy is applied to (allowed value: all) 33 | - `id` (String) ID of the resource in the terraform state 34 | - `member_group` (List of String) List of user groups member of this hbac policy. 35 | - `member_host` (List of String) List of hosts member of this hbac policy. 36 | - `member_hostgroup` (List of String) List of host groups member of this hbac policy. 37 | - `member_service` (List of String) List of services member of this hbac policy. 38 | - `member_servicegroup` (List of String) List of service groups member of this hbac policy. 39 | - `member_user` (List of String) List of users member of this hbac policy. 40 | - `servicecategory` (String) Command category the hbac policy is applied to (allowed value: all) 41 | - `usercategory` (String) User category the hbac policy is applied to (allowed value: all) 42 | -------------------------------------------------------------------------------- /docs/resources/sudo_rule.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_sudo_rule Resource - freeipa" 3 | description: |- 4 | FreeIPA Sudo rule resource 5 | --- 6 | 7 | # freeipa_sudo_rule (Resource) 8 | 9 | FreeIPA Sudo rule resource 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | resource "freeipa_sudo_rule" "sysadmins" { 16 | name = "sysadmins" 17 | description = "Sysadmins have all permissions" 18 | } 19 | ``` 20 | 21 | 22 | 23 | ## Import Usage 24 | 25 | ```terraform 26 | import { 27 | to = freeipa_sudo_rule.testsudorule 28 | id = "testsudorule" 29 | } 30 | 31 | resource "freeipa_sudo_rule" "testsudorule" { 32 | name = "testsudorule" 33 | } 34 | ``` 35 | 36 | 37 | 38 | ## Schema 39 | 40 | ### Required 41 | 42 | - `name` (String) Name of the sudo rule 43 | 44 | ### Optional 45 | 46 | - `commandcategory` (String) Command category the sudo rule is applied to (allowed value: all) 47 | - `description` (String) Sudo rule description 48 | - `enabled` (Boolean) Enable this sudo rule 49 | - `hostcategory` (String) Host category the sudo rule is applied to (allowed value: all) 50 | - `order` (Number) Sudo rule order (must be unique) 51 | - `runasgroupcategory` (String) Run as group category the sudo rule is applied to (allowed value: all) 52 | - `runasusercategory` (String) Run as user category the sudo rule is applied to (allowed value: all) 53 | - `usercategory` (String) User category the sudo rule is applied to (allowed value: all) 54 | 55 | ### Read-Only 56 | 57 | - `id` (String) ID of the resource 58 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Feature Request" 3 | about: "I have a suggestion (and might want to implement myself \U0001F642)!" 4 | title: '' 5 | labels: enhancement 6 | 7 | --- 8 | 9 | 10 | ### Description 11 | 12 | 13 | 14 | ### Potential Terraform Configuration 15 | 16 | 17 | 18 | ```hcl 19 | # Copy-paste your Terraform configurations here - for large Terraform configs, 20 | # please use a service like Dropbox and share a link to the ZIP file. For 21 | # security, you can also encrypt the files using our GPG public key. 22 | ``` 23 | 24 | ### References 25 | 26 | 31 | 32 | 33 | 34 | ### Community Note 35 | 36 | * Please vote on this issue by adding a 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to the original issue to help the community and maintainers prioritize this request 37 | * If you are interested in working on this issue or have submitted a pull request, please leave a comment 38 | 39 | -------------------------------------------------------------------------------- /docs/resources/sudo_rule_runasuser_membership.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_sudo_rule_runasuser_membership Resource - freeipa" 3 | description: |- 4 | FreeIPA Sudo rule run as user membership resource. 5 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 6 | --- 7 | 8 | # freeipa_sudo_rule_runasuser_membership (Resource) 9 | 10 | FreeIPA Sudo rule run as user membership resource. 11 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 12 | 13 | 14 | ## Example Usage 15 | 16 | ```terraform 17 | resource "freeipa_sudo_rule_runasuser_membership" "user-0" { 18 | name = "sudo-rule-test" 19 | runasuser = "user01" 20 | } 21 | 22 | resource "freeipa_sudo_rule_runasuser_membership" "users-0" { 23 | name = "sudo-rule-test" 24 | runasusers = ["user01", "user02"] 25 | identifier = "users-0" 26 | } 27 | ``` 28 | 29 | 30 | 31 | 32 | 33 | ## Schema 34 | 35 | ### Required 36 | 37 | - `name` (String) Sudo rule name 38 | 39 | ### Optional 40 | 41 | - `identifier` (String) Unique identifier to differentiate multiple sudo rule runasuser membership resources on the same sudo rule. Manadatory for using runasusers configurations. 42 | - `runasuser` (String, Deprecated) **deprecated** Run As User to add to the sudo rule. Can be an external user (local user of ipa clients) 43 | - `runasusers` (List of String) List of Run As User to add to the sudo rule. Can be an external user (local user of ipa clients) 44 | 45 | ### Read-Only 46 | 47 | - `id` (String) ID of the resource 48 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # Visit https://goreleaser.com for documentation on how to customize this 2 | # behavior. 3 | version: 2 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 HCP Terraform 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 | - freebsd 21 | - windows 22 | - linux 23 | - darwin 24 | goarch: 25 | - amd64 26 | - '386' 27 | - arm 28 | - arm64 29 | ignore: 30 | - goos: darwin 31 | goarch: '386' 32 | binary: '{{ .ProjectName }}_v{{ .Version }}' 33 | archives: 34 | - format: zip 35 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' 36 | checksum: 37 | name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' 38 | algorithm: sha256 39 | signs: 40 | - artifacts: checksum 41 | args: 42 | # if you are using this in a GitHub action or some other automated pipeline, you 43 | # need to pass the batch flag to indicate its not interactive. 44 | - "--batch" 45 | - "--local-user" 46 | - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key 47 | - "--output" 48 | - "${signature}" 49 | - "--detach-sign" 50 | - "${artifact}" 51 | release: 52 | draft: true 53 | changelog: 54 | disable: true -------------------------------------------------------------------------------- /docs/data-sources/hostgroup.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_hostgroup Data Source - freeipa" 3 | description: |- 4 | FreeIPA Host Group data source 5 | --- 6 | 7 | # freeipa_hostgroup (Data Source) 8 | 9 | FreeIPA Host Group data source 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | data "freeipa_hostgroup" "hostgroup-0" { 16 | name = "testhostgroup" 17 | } 18 | ``` 19 | 20 | 21 | 22 | ## Schema 23 | 24 | ### Required 25 | 26 | - `name` (String) Hostgroup name 27 | 28 | ### Read-Only 29 | 30 | - `description` (String) Hostgroup Description 31 | - `id` (String) ID of the resource in the terraform state 32 | - `member_host` (List of String) List of hosts that are member of this hostgroup. 33 | - `member_hostgroup` (List of String) List of hostgroups that are member of this hostgroup. 34 | - `member_indirect_host` (List of String) List of hosts that are is indirectly member of this hostgroup. 35 | - `member_indirect_hostgroup` (List of String) List of hostgroups that are is indirectly member of this hostgroup. 36 | - `memberof_hbacrule` (List of String) List of HBAC rules this hostgroup is member of. 37 | - `memberof_hostgroup` (List of String) List of hostgroups this hostgroup is member of. 38 | - `memberof_indirect_hbacrule` (List of String) List of HBAC rules this hostgroup is indirectly member of. 39 | - `memberof_indirect_hostgroup` (List of String) List of hostgroups this hostgroup is is indirectly member of. 40 | - `memberof_indirect_sudorule` (List of String) List of SUDO rules this hostgroup is is indirectly member of. 41 | - `memberof_sudorule` (List of String) List of SUDO rules this hostgroup is member of. 42 | -------------------------------------------------------------------------------- /docs/resources/sudo_rule_runasgroup_membership.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_sudo_rule_runasgroup_membership Resource - freeipa" 3 | description: |- 4 | FreeIPA Sudo rule run as group membership resource. 5 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 6 | --- 7 | 8 | # freeipa_sudo_rule_runasgroup_membership (Resource) 9 | 10 | FreeIPA Sudo rule run as group membership resource. 11 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 12 | 13 | 14 | ## Example Usage 15 | 16 | ```terraform 17 | resource "freeipa_sudo_rule_runasgroup_membership" "group-0" { 18 | name = "sudo-rule-test" 19 | runasgroup = "group01" 20 | } 21 | 22 | resource "freeipa_sudo_rule_runasgroup_membership" "groups-0" { 23 | name = "sudo-rule-test" 24 | runasgroups = ["group01", "group02"] 25 | identifier = "groups-0" 26 | } 27 | ``` 28 | 29 | 30 | 31 | 32 | 33 | ## Schema 34 | 35 | ### Required 36 | 37 | - `name` (String) Sudo rule name 38 | 39 | ### Optional 40 | 41 | - `identifier` (String) Unique identifier to differentiate multiple sudo rule runasgroup membership resources on the same sudo rule. Manadatory for using runasgroups configurations. 42 | - `runasgroup` (String, Deprecated) **deprecated** Run As Group to add to the sudo rule. Can be an external group (local group of ipa clients) 43 | - `runasgroups` (List of String) List of Run As Group to add to the sudo rule. Can be an external group (local group of ipa clients) 44 | 45 | ### Read-Only 46 | 47 | - `id` (String) ID of the resource 48 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Provider: FREEIPA" 3 | description: |- 4 | This FreeIPA terraform provider allows to manage resources for the FreeIPA solution. 5 | The provider is also compatible with the Red Hat Identity Manager product that is based on FreeIPA. 6 | --- 7 | 8 | # FREEIPA Provider 9 | 10 | This FreeIPA terraform provider allows to manage resources for the `FreeIPA` solution. 11 | The provider is also compatible with the `Red Hat Identity Manager` product that is based on FreeIPA. 12 | 13 | The provider needs to be configured with the proper endpoints and credentials before it can be used. 14 | 15 | ## Example Usage 16 | 17 | ```terraform 18 | terraform { 19 | required_providers { 20 | freeipa = { 21 | version = "5.1.0" 22 | source = "rework-space-com/freeipa" 23 | } 24 | } 25 | } 26 | 27 | provider "freeipa" { 28 | host = "ipa.example.test" 29 | username = "admin" 30 | password = "123456789" 31 | insecure = true 32 | } 33 | ``` 34 | 35 | 36 | ## Schema 37 | 38 | ### Required 39 | 40 | - `host` (String) The FreeIPA host. Can be set through the environment variable `FREEIPA_HOST`. 41 | 42 | ### Optional 43 | 44 | - `ca_certificate` (String) Path to the server's SSL CA certificate. Can be set through the environment variable `FREEIPA_CA_CERT`. 45 | - `insecure` (Boolean) Whether to verify the server's SSL certificate. Can be set through the environment variable `FREEIPA_INSECURE`. 46 | - `password` (String, Sensitive) Password to use for connection. Can be set through the environment variable `FREEIPA_PASSWORD`. 47 | - `username` (String) Username to use for connection. Can be set through the environment variable `FREEIPA_USERNAME`. 48 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # This GitHub action can publish assets for release when a tag is created. 2 | # Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0). 3 | # 4 | # This uses an action (hashicorp/ghaction-import-gpg) that assumes you set your 5 | # private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `PASSPHRASE` 6 | # secret. If you would rather own your own GPG handling, please fork this action 7 | # or use an alternative one for key handling. 8 | # 9 | # You will need to pass the `--batch` flag to `gpg` in your signing step 10 | # in `goreleaser` to indicate this is being used in a non-interactive mode. 11 | # 12 | name: release 13 | on: 14 | push: 15 | tags: 16 | - 'v*' 17 | jobs: 18 | goreleaser: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - 22 | name: Checkout 23 | uses: actions/checkout@v4 24 | - 25 | name: Unshallow 26 | run: git fetch --prune --unshallow 27 | - 28 | name: Set up Go 29 | uses: actions/setup-go@v5 30 | with: 31 | go-version-file: 'go.mod' 32 | cache: true 33 | - 34 | name: Import GPG key 35 | id: import_gpg 36 | uses: crazy-max/ghaction-import-gpg@v5.2.0 37 | with: 38 | GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} 39 | PASSPHRASE: ${{ secrets.PASSPHRASE }} 40 | - 41 | name: Run GoReleaser 42 | uses: goreleaser/goreleaser-action@v6 43 | with: 44 | version: latest 45 | args: release --clean 46 | env: 47 | GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} 48 | # GitHub sets this automatically 49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 50 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Authors: 2 | // 3 | // Antoine Gatineau 4 | // Roman Butsiy 5 | // 6 | // SPDX-License-Identifier: GPL-3.0-only 7 | 8 | package main 9 | 10 | import ( 11 | "context" 12 | "flag" 13 | "log" 14 | 15 | "github.com/hashicorp/terraform-plugin-framework/providerserver" 16 | 17 | "github.com/rework-space-com/terraform-provider-freeipa/freeipa" 18 | ) 19 | 20 | // Run "go generate" to format example terraform files and generate the docs for the registry/website 21 | 22 | // If you do not have terraform installed, you can remove the formatting command, but it's suggested to 23 | // ensure the documentation is formatted properly. 24 | //go:generate terraform fmt -recursive ./examples/ 25 | 26 | // Run the docs generation tool, check its repository for more information on how it works and how docs 27 | // can be customized. 28 | //go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs generate -provider-name freeipa 29 | 30 | var ( 31 | // these will be set by the goreleaser configuration 32 | // to appropriate values for the compiled binary. 33 | version string = "dev" 34 | 35 | // goreleaser can pass other information to the main package, such as the specific commit 36 | // https://goreleaser.com/cookbooks/using-main.version/ 37 | ) 38 | 39 | func main() { 40 | var debug bool 41 | 42 | flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") 43 | flag.Parse() 44 | 45 | opts := providerserver.ServeOpts{ 46 | Address: "registry.terraform.io/rework-space-com/freeipa", 47 | Debug: debug, 48 | } 49 | 50 | err := providerserver.Serve(context.Background(), freeipa.New(version), opts) 51 | 52 | if err != nil { 53 | log.Fatal(err.Error()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /docker_compose/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | version: "3" 19 | 20 | services: 21 | ipa: 22 | image: freeipa/freeipa-server:fedora-42-4.12.5 23 | container_name: ipa 24 | hostname: ipa.ipatest.lan 25 | restart: unless-stopped 26 | environment: 27 | container: "docker" 28 | IPA_SERVER_HOSTNAME: "ipa.ipatest.lan" 29 | IPA_SERVER_INSTALL_OPTS: '--no-ntp --ds-password=P@ssword --admin-password=P@ssword --domain=ipatest.lan --realm=IPATEST.LAN --no-forwarders --setup-dns --no-dnssec-validation --allow-zone-overlap --no-reverse --unattended' 30 | ports: 31 | - "80:80" 32 | - "443:443" 33 | sysctls: 34 | - net.ipv6.conf.all.disable_ipv6=0 35 | - net.ipv6.conf.lo.disable_ipv6=0 36 | cap_add: 37 | - NET_ADMIN 38 | privileged: true 39 | networks: 40 | ipa_network: 41 | volumes: 42 | - "ipa-data:/data" 43 | 44 | 45 | networks: 46 | ipa_network: 47 | driver: bridge 48 | 49 | 50 | volumes: 51 | ipa-data: 52 | -------------------------------------------------------------------------------- /docs/resources/host_hostgroup_membership.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_host_hostgroup_membership Resource - freeipa" 3 | description: |- 4 | FreeIPA User Group Membership resource. 5 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 6 | --- 7 | 8 | # freeipa_host_hostgroup_membership (Resource) 9 | 10 | FreeIPA User Group Membership resource. 11 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 12 | 13 | 14 | ## Example Usage 15 | 16 | ```terraform 17 | resource "freeipa_host_hostgroup_membership" "test-0" { 18 | name = "test-hostgroup-2" 19 | host = "test.example.test" 20 | } 21 | 22 | resource "freeipa_host_hostgroup_membership" "test-1" { 23 | name = "test-hostgroup-2" 24 | hostgroup = "test-hostgroup" 25 | } 26 | 27 | resource "freeipa_host_hostgroup_membership" "test-2" { 28 | name = "test-hostgroup-2" 29 | hosts = ["host1", "host2"] 30 | hostgroups = ["test-hostgroup", "test-hostgroup2"] 31 | identifier = "my_unique_identifier" 32 | } 33 | ``` 34 | 35 | 36 | 37 | 38 | 39 | ## Schema 40 | 41 | ### Required 42 | 43 | - `name` (String) Hostgroup name 44 | 45 | ### Optional 46 | 47 | - `host` (String, Deprecated) **deprecated** Host to add. Will be replaced by hosts. 48 | - `hostgroup` (String, Deprecated) **deprecated** Hostgroup to add. Will be replaced by hostgroups. 49 | - `hostgroups` (List of String) Hostgroups to add as hostgroup members 50 | - `hosts` (List of String) Hosts to add as hostgroup members 51 | - `identifier` (String) Unique identifier to differentiate multiple hostgroup membership resources on the same hostgroup. Manadatory for using hosts/hostgroups configurations. 52 | 53 | ### Read-Only 54 | 55 | - `id` (String) ID of the resource 56 | -------------------------------------------------------------------------------- /docs/resources/sudo_rule_user_membership.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_sudo_rule_user_membership Resource - freeipa" 3 | description: |- 4 | FreeIPA Sudo rule user membership resource. 5 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 6 | --- 7 | 8 | # freeipa_sudo_rule_user_membership (Resource) 9 | 10 | FreeIPA Sudo rule user membership resource. 11 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 12 | 13 | 14 | ## Example Usage 15 | 16 | ```terraform 17 | resource "freeipa_sudo_rule_user_membership" "user-0" { 18 | name = "sudo-rule-test" 19 | user = "user01" 20 | } 21 | 22 | resource "freeipa_sudo_rule_user_membership" "users-1" { 23 | name = "sudo-rule-test" 24 | users = ["user01"] 25 | identifier = "users-1" 26 | } 27 | 28 | resource "freeipa_sudo_rule_user_membership" "group-3" { 29 | name = "sudo-rule-test" 30 | group = "test-group-0" 31 | } 32 | 33 | resource "freeipa_sudo_rule_user_membership" "groups-3" { 34 | name = "sudo-rule-test" 35 | groups = ["test-group-0"] 36 | identifier = "groups-3" 37 | } 38 | ``` 39 | 40 | 41 | 42 | 43 | 44 | ## Schema 45 | 46 | ### Required 47 | 48 | - `name` (String) Sudo rule name 49 | 50 | ### Optional 51 | 52 | - `group` (String, Deprecated) **deprecated** User group to add to the sudo rule 53 | - `groups` (List of String) List of user groups to add to the sudo rule 54 | - `identifier` (String) Unique identifier to differentiate multiple sudo rule user membership resources on the same sudo rule. Manadatory for using users/groups configurations. 55 | - `user` (String, Deprecated) **deprecated** User to add to the sudo rule 56 | - `users` (List of String) List of users to add to the sudo rule 57 | 58 | ### Read-Only 59 | 60 | - `id` (String) ID of the resource 61 | -------------------------------------------------------------------------------- /docs/resources/sudo_cmdgroup_membership.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_sudo_cmdgroup_membership Resource - freeipa" 3 | description: |- 4 | FreeIPA Sudo command group membership resource. 5 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 6 | --- 7 | 8 | # freeipa_sudo_cmdgroup_membership (Resource) 9 | 10 | FreeIPA Sudo command group membership resource. 11 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 12 | 13 | 14 | ## Example Usage 15 | 16 | ```terraform 17 | resource "freeipa_sudo_cmd" "bash" { 18 | name = "/bin/bash" 19 | description = "The bash shell" 20 | } 21 | 22 | resource "freeipa_sudo_cmd" "fish" { 23 | name = "/bin/fish" 24 | description = "The fish shell" 25 | } 26 | 27 | resource "freeipa_sudo_cmdgroup" "terminals" { 28 | name = "terminals" 29 | description = "The terminals allowed to be sudoed" 30 | } 31 | 32 | resource "freeipa_sudo_cmdgroup_membership" "terminal_bash" { 33 | name = freeipa_sudo_cmdgroup.terminals.id 34 | sudocmd = freeipa_sudo_cmd.bash.id 35 | } 36 | 37 | resource "freeipa_sudo_cmdgroup_membership" "terminal_fish" { 38 | name = freeipa_sudo_cmdgroup.terminals.id 39 | sudocmd = freeipa_sudo_cmd.fish.id 40 | } 41 | ``` 42 | 43 | 44 | 45 | 46 | 47 | ## Schema 48 | 49 | ### Required 50 | 51 | - `name` (String) Name of the sudo command group 52 | 53 | ### Optional 54 | 55 | - `identifier` (String) Unique identifier to differentiate multiple sudo command group membership resources on the same sudo command group. Manadatory for using sudocmds configurations. 56 | - `sudocmd` (String, Deprecated) **deprecated** Sudo command to add as a member 57 | - `sudocmds` (List of String) List of sudo command to add as a member 58 | 59 | ### Read-Only 60 | 61 | - `id` (String) ID of the resource 62 | -------------------------------------------------------------------------------- /docs/resources/sudo_rule_host_membership.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_sudo_rule_host_membership Resource - freeipa" 3 | description: |- 4 | FreeIPA Sudo rule host membership resource. 5 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 6 | --- 7 | 8 | # freeipa_sudo_rule_host_membership (Resource) 9 | 10 | FreeIPA Sudo rule host membership resource. 11 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 12 | 13 | 14 | ## Example Usage 15 | 16 | ```terraform 17 | resource "freeipa_sudo_rule_host_membership" "host-0" { 18 | name = "sudo-rule-test" 19 | host = "test.example.test" 20 | } 21 | 22 | resource "freeipa_sudo_rule_host_membership" "hosts-0" { 23 | name = "sudo-rule-test" 24 | hosts = ["test.example.test"] 25 | identifier = "hosts-0" 26 | } 27 | 28 | resource "freeipa_sudo_rule_host_membership" "hostgroup-3" { 29 | name = "sudo-rule-test" 30 | hostgroup = "test-hostgroup" 31 | } 32 | 33 | resource "freeipa_sudo_rule_host_membership" "hostgroups-3" { 34 | name = "sudo-rule-test" 35 | hostgroups = ["test-hostgroup"] 36 | identifier = "hostgroups-3" 37 | } 38 | ``` 39 | 40 | 41 | 42 | 43 | 44 | ## Schema 45 | 46 | ### Required 47 | 48 | - `name` (String) Sudo rule name 49 | 50 | ### Optional 51 | 52 | - `host` (String, Deprecated) **deprecated** Host to add to the sudo rule 53 | - `hostgroup` (String, Deprecated) **deprecated** Hostgroup to add to the sudo rule 54 | - `hostgroups` (List of String) List of hostgroups to add to the sudo rule 55 | - `hosts` (List of String) List of hosts to add to the sudo rule 56 | - `identifier` (String) Unique identifier to differentiate multiple sudo rule host membership resources on the same sudo rule. Manadatory for using hosts/hostgroups configurations. 57 | 58 | ### Read-Only 59 | 60 | - `id` (String) ID of the resource 61 | -------------------------------------------------------------------------------- /docs/data-sources/group.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_group Data Source - freeipa" 3 | description: |- 4 | FreeIPA User Group data source 5 | --- 6 | 7 | # freeipa_group (Data Source) 8 | 9 | FreeIPA User Group data source 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | data "freeipa_group" "group-0" { 16 | name = "test-group" 17 | } 18 | ``` 19 | 20 | 21 | 22 | ## Schema 23 | 24 | ### Required 25 | 26 | - `name` (String) Group name 27 | 28 | - The name must not exceed 32 characters. 29 | - The name must contain only lowercase letters (a-z), digits (0-9), and the characters (. - _). 30 | - The name must not start with a special character. 31 | - A user and a group cannot have the same name. 32 | 33 | ### Read-Only 34 | 35 | - `description` (String) Group Description 36 | - `gid_number` (Number) GID (use this option to set it manually) 37 | - `id` (String) ID of the resource in the terraform state 38 | - `member_external` (List of String) List of external users (from trusted domain) that are member of this group. 39 | - `member_group` (List of String) List of groups that are member of this group. 40 | - `member_indirect_group` (List of String) List of groups that are is indirectly member of this group. 41 | - `member_indirect_user` (List of String) List of users that are is indirectly member of this group. 42 | - `member_user` (List of String) List of users that are member of this group. 43 | - `memberof_group` (List of String) List of groups this group is member of. 44 | - `memberof_hbacrule` (List of String) List of HBAC rules this group is member of. 45 | - `memberof_indirect_group` (List of String) List of groups this group is is indirectly member of. 46 | - `memberof_indirect_hbacrule` (List of String) List of HBAC rules this group is indirectly member of. 47 | - `memberof_indirect_sudorule` (List of String) List of SUDO rules this group is is indirectly member of. 48 | - `memberof_sudorule` (List of String) List of SUDO rules this group is member of. 49 | -------------------------------------------------------------------------------- /docs/resources/sudo_rule_allowcmd_membership.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_sudo_rule_allowcmd_membership Resource - freeipa" 3 | description: |- 4 | FreeIPA Sudo rule allow command membership resource. 5 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 6 | --- 7 | 8 | # freeipa_sudo_rule_allowcmd_membership (Resource) 9 | 10 | FreeIPA Sudo rule allow command membership resource. 11 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 12 | 13 | 14 | ## Example Usage 15 | 16 | ```terraform 17 | resource "freeipa_sudo_rule_allowcmd_membership" "allowed_cmd" { 18 | name = "sudo-rule-editors" 19 | sudocmd = "/bin/bash" 20 | } 21 | 22 | resource "freeipa_sudo_rule_allowcmd_membership" "allowed_cmd" { 23 | name = "sudo-rule-editors" 24 | sudocmds = ["/bin/bash"] 25 | identifier = "allowed_bash" 26 | } 27 | 28 | resource "freeipa_sudo_rule_allowcmd_membership" "allowed_cmdgrp" { 29 | name = "sudo-rule-editors" 30 | sudocmd_group = "allowed-terminals" 31 | } 32 | 33 | resource "freeipa_sudo_rule_allowcmd_membership" "allowed_cmdgrp" { 34 | name = "sudo-rule-editors" 35 | sudocmd_groups = ["allowed-terminals"] 36 | identifier = "allowed_terminals" 37 | } 38 | ``` 39 | 40 | 41 | 42 | 43 | 44 | ## Schema 45 | 46 | ### Required 47 | 48 | - `name` (String) Sudo rule name 49 | 50 | ### Optional 51 | 52 | - `identifier` (String) Unique identifier to differentiate multiple sudo rule denied membership resources on the same sudo rule. Manadatory for using sudocmds/sudocmd_groups configurations. 53 | - `sudocmd` (String, Deprecated) **deprecated** Sudo command to allow by the sudo rule 54 | - `sudocmd_group` (String, Deprecated) **deprecated** Sudo command group to allow by the sudo rule 55 | - `sudocmd_groups` (List of String) List of sudo command group to allow by the sudo rule 56 | - `sudocmds` (List of String) List of Sudo command to allow by the sudo rule 57 | 58 | ### Read-Only 59 | 60 | - `id` (String) ID of the resource 61 | -------------------------------------------------------------------------------- /docs/resources/sudo_rule_denycmd_membership.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_sudo_rule_denycmd_membership Resource - freeipa" 3 | description: |- 4 | FreeIPA Sudo rule deny command membership resource. 5 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 6 | --- 7 | 8 | # freeipa_sudo_rule_denycmd_membership (Resource) 9 | 10 | FreeIPA Sudo rule deny command membership resource. 11 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 12 | 13 | 14 | ## Example Usage 15 | 16 | ```terraform 17 | resource "freeipa_sudo_rule_denycmd_membership" "denied_cmd" { 18 | name = "sudo-rule-restricted" 19 | sudocmd = "/usr/bin/systemctl" 20 | } 21 | 22 | resource "freeipa_sudo_rule_denycmd_membership" "denied_cmd" { 23 | name = "sudo-rule-restricted" 24 | sudocmds = ["/usr/bin/systemctl"] 25 | identifier = "denied_systemctl" 26 | } 27 | 28 | resource "freeipa_sudo_rule_denycmd_membership" "denied_cmdgrp" { 29 | name = "sudo-rule-restricted" 30 | sudocmd_group = "service-management" 31 | } 32 | 33 | resource "freeipa_sudo_rule_denycmd_membership" "denied_cmdgrp" { 34 | name = "sudo-rule-restricted" 35 | sudocmd_groups = ["service-management"] 36 | identifier = "denied_services" 37 | } 38 | ``` 39 | 40 | 41 | 42 | 43 | 44 | ## Schema 45 | 46 | ### Required 47 | 48 | - `name` (String) Sudo rule name 49 | 50 | ### Optional 51 | 52 | - `identifier` (String) Unique identifier to differentiate multiple sudo rule allowed membership resources on the same sudo rule. Manadatory for using sudocmds/sudocmd_groups configurations. 53 | - `sudocmd` (String, Deprecated) **deprecated** Sudo command to deny by the sudo rule 54 | - `sudocmd_group` (String, Deprecated) **deprecated** Sudo command group to deny by the sudo rule 55 | - `sudocmd_groups` (List of String) List of sudo command group to deny by the sudo rule 56 | - `sudocmds` (List of String) List of Sudo command to deny by the sudo rule 57 | 58 | ### Read-Only 59 | 60 | - `id` (String) ID of the resource 61 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug Report" 3 | about: "If something isn't working as expected \U0001F914." 4 | title: '' 5 | labels: bug 6 | 7 | --- 8 | 9 | 14 | 15 | 16 | ### Terraform Version, Provider Version and FreeIPA version 17 | 18 | ``` 19 | Terraform version: 20 | FreeIPA provider version: 21 | FreeIPA version: 22 | ``` 23 | 24 | ### Affected Resource(s) 25 | 29 | 30 | ### Terraform Configuration Files 31 | ```hcl 32 | # Copy-paste your Terraform configurations here. 33 | ``` 34 | 35 | ### Debug Output 36 | 37 | 38 | ### Panic Output 39 | 40 | 41 | ### Steps to Reproduce 42 | 44 | 45 | ### Expected Behavior 46 | What should have happened? 47 | 48 | ### Actual Behavior 49 | What actually happened? 50 | 51 | ### Important Factoids 52 | 53 | 54 | ### References 55 | 56 | - GH-1234 57 | 58 | ### Community Note 59 | 60 | * Please vote on this issue by adding a 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to the original issue to help the community and maintainers prioritize this request 61 | * If you are interested in working on this issue or have submitted a pull request, please leave a comment -------------------------------------------------------------------------------- /docs/resources/hbac_policy_host_membership.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_hbac_policy_host_membership Resource - freeipa" 3 | description: |- 4 | FreeIPA HBAC policy host membership resource. 5 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 6 | --- 7 | 8 | # freeipa_hbac_policy_host_membership (Resource) 9 | 10 | FreeIPA HBAC policy host membership resource. 11 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 12 | 13 | 14 | ## Example Usage 15 | 16 | ```terraform 17 | resource "freeipa_hbac_policy" "hbac-0" { 18 | name = "test-hbac" 19 | description = "Test HBAC policy" 20 | enabled = true 21 | } 22 | 23 | resource "freeipa_hbac_policy_host_membership" "hbac-host-1" { 24 | name = "test-hbac" 25 | host = "ipaclient1.ipatest.lan" 26 | } 27 | 28 | resource "freeipa_hbac_policy_host_membership" "hbac-hosts-1" { 29 | name = "test-hbac" 30 | hosts = ["ipaclient1.ipatest.lan", "ipaclient2.ipatest.lan"] 31 | identifier = "hbac-hosts-1" 32 | } 33 | 34 | resource "freeipa_hbac_policy_host_membership" "hostgroup-3" { 35 | name = "test-hbac" 36 | hostgroup = "test-hostgroup" 37 | } 38 | 39 | resource "freeipa_hbac_policy_host_membership" "hostgroups-3" { 40 | name = "test-hbac" 41 | hostgroups = ["test-hostgroup", "test-hostgroup-2"] 42 | identifier = "hostgroups-3" 43 | } 44 | ``` 45 | 46 | 47 | 48 | 49 | 50 | ## Schema 51 | 52 | ### Required 53 | 54 | - `name` (String) HBAC policy name 55 | 56 | ### Optional 57 | 58 | - `host` (String, Deprecated) **deprecated** Host to add to the HBAC policy 59 | - `hostgroup` (String, Deprecated) **deprecated** Hostgroup to add to the HBAC policy 60 | - `hostgroups` (List of String) List of hostgroups to add to the HBAC policy 61 | - `hosts` (List of String) List of hosts to add to the HBAC policy 62 | - `identifier` (String) Unique identifier to differentiate multiple HBAC policy host membership resources on the same HBAC policy. Manadatory for using hosts/hostgroups configurations. 63 | 64 | ### Read-Only 65 | 66 | - `id` (String) ID of the resource 67 | -------------------------------------------------------------------------------- /docs/resources/hbac_policy_user_membership.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_hbac_policy_user_membership Resource - freeipa" 3 | description: |- 4 | FreeIPA HBAC policy host membership resource. 5 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 6 | --- 7 | 8 | # freeipa_hbac_policy_user_membership (Resource) 9 | 10 | FreeIPA HBAC policy host membership resource. 11 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 12 | 13 | 14 | ## Example Usage 15 | 16 | ```terraform 17 | resource "freeipa_hbac_policy" "hbac-0" { 18 | name = "test-hbac" 19 | description = "Test HBAC policy" 20 | enabled = true 21 | hostcategory = "all" 22 | servicecategory = "all" 23 | } 24 | 25 | resource "freeipa_hbac_policy_user_membership" "hbac-user-1" { 26 | name = "test-hbac" 27 | user = "user-1" 28 | } 29 | 30 | resource "freeipa_hbac_policy_user_membership" "hbac-users-1" { 31 | name = "test-hbac" 32 | users = ["user-2", "user-3"] 33 | identifier = "hbac-users-1" 34 | } 35 | 36 | resource "freeipa_hbac_policy_user_membership" "hbac-group-1" { 37 | name = "test-hbac" 38 | group = "usergroup-1" 39 | } 40 | 41 | resource "freeipa_hbac_policy_user_membership" "hbac-groups-1" { 42 | name = "test-hbac" 43 | groups = ["usergroup-2", "usergroup-3"] 44 | identifier = "hbac-groups-1" 45 | } 46 | ``` 47 | 48 | 49 | 50 | 51 | 52 | ## Schema 53 | 54 | ### Required 55 | 56 | - `name` (String) HBAC policy name 57 | 58 | ### Optional 59 | 60 | - `group` (String, Deprecated) **deprecated** User group to add to the HBAC policy 61 | - `groups` (List of String) List of user groups to add to the HBAC policy 62 | - `identifier` (String) Unique identifier to differentiate multiple HBAC policy user membership resources on the same HBAC policy. Manadatory for using users/groups configurations. 63 | - `user` (String, Deprecated) **deprecated** User FDQN the policy is applied to 64 | - `users` (List of String) List of user FQDNs to add to the HBAC policy 65 | 66 | ### Read-Only 67 | 68 | - `id` (String) ID of the resource 69 | -------------------------------------------------------------------------------- /docs/data-sources/sudo_rule.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_sudo_rule Data Source - freeipa" 3 | description: |- 4 | FreeIPA Sudo rule data source 5 | --- 6 | 7 | # freeipa_sudo_rule (Data Source) 8 | 9 | FreeIPA Sudo rule data source 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | data "freeipa_sudo_rule" "operators" { 16 | name = "operators" 17 | } 18 | ``` 19 | 20 | 21 | 22 | ## Schema 23 | 24 | ### Required 25 | 26 | - `name` (String) Name of the sudo rule 27 | 28 | ### Read-Only 29 | 30 | - `commandcategory` (String) Command category the sudo rule is applied to (allowed value: all) 31 | - `description` (String) Description of the sudo rule 32 | - `enabled` (Boolean) Enable this sudo rule 33 | - `hostcategory` (String) Host category the sudo rule is applied to (allowed value: all) 34 | - `id` (String) ID of the resource in the terraform state 35 | - `member_allow_sudo_cmd` (List of String) List of allowed sudo commands member of this sudo rule. 36 | - `member_allow_sudo_cmdgroup` (List of String) List of allowed sudo command groups member of this sudo rule. 37 | - `member_deny_sudo_cmd` (List of String) List of denied sudo commands member of this sudo rule. 38 | - `member_deny_sudo_cmdgroup` (List of String) List of denied sudo command groups member of this sudo rule. 39 | - `member_group` (List of String) List of user groups member of this sudo rule. 40 | - `member_host` (List of String) List of hosts member of this sudo rule. 41 | - `member_hostgroup` (List of String) List of host groups member of this sudo rule. 42 | - `member_user` (List of String) List of users member of this sudo rule. 43 | - `option` (List of String) List of options defined for this sudo rule. 44 | - `order` (Number) Sudo rule order (must be unique) 45 | - `runasgroup` (List of String) List of groups authorised to be run as. 46 | - `runasgroupcategory` (String) Run as group category the sudo rule is applied to (allowed value: all) 47 | - `runasuser` (List of String) List of users authorised to be run as. 48 | - `runasusercategory` (String) Run as user category the sudo rule is applied to (allowed value: all) 49 | - `usercategory` (String) User category the sudo rule is applied to (allowed value: all) 50 | -------------------------------------------------------------------------------- /.github/workflows/test-acc.yml: -------------------------------------------------------------------------------- 1 | name: Terraform Acceptance Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'feature/**' 7 | - 'feat/**' 8 | - 'fix/**' 9 | - 'chore/**' 10 | - main 11 | pull_request: 12 | paths: 13 | - '.github/workflows/test-acc.yml' 14 | - '**.go' 15 | 16 | permissions: 17 | # Permission for checking out code 18 | contents: read 19 | 20 | jobs: 21 | acceptance: 22 | name: Acceptance Tests 23 | runs-on: ubuntu-latest 24 | services: 25 | ipa: 26 | image: freeipa/freeipa-server:fedora-42-4.12.2 27 | options: '--hostname ipa.ipatest.lan --privileged --sysctl net.ipv6.conf.all.disable_ipv6=0 --sysctl net.ipv6.conf.lo.disable_ipv6=0 --cap-add=NET_ADMIN' 28 | ports: 29 | - '80:80' 30 | - '443:443' 31 | env: 32 | container: "docker" 33 | IPA_SERVER_HOSTNAME: "ipa.ipatest.lan" 34 | IPA_SERVER_INSTALL_OPTS: '--no-ntp --ds-password=P@ssword --admin-password=P@ssword --domain=ipatest.lan --realm=IPATEST.LAN --no-forwarders --setup-dns --no-dnssec-validation --allow-zone-overlap --no-reverse --unattended' 35 | 36 | steps: 37 | - uses: actions/checkout@v4 38 | - uses: actions/setup-go@v5 39 | with: 40 | go-version: '1.22' 41 | - uses: hashicorp/setup-terraform@v3 42 | with: 43 | terraform_version: '1.9.*' 44 | terraform_wrapper: false 45 | - name: Add ipa container to /etc/hosts 46 | run: | 47 | sudo echo "127.0.0.1 ipa.ipatest.lan" | sudo tee -a /etc/hosts 48 | - name: Run acceptance tests 49 | run: | 50 | docker logs "${{ job.services.ipa.id }}" 51 | echo "Waiting for FreeIPA Server to initialize" 52 | while [ "$(docker logs --tail 1 ${{ job.services.ipa.id }})" != "FreeIPA server configured." ] ; do docker logs --tail 1 ${{ job.services.ipa.id }} ; echo "FreeIPA server not ready. Waiting another 30s." ; sleep 30 ; done 53 | go test -v -cover ./freeipa/ 54 | env: 55 | TF_ACC: '1' 56 | FREEIPA_HOST: 'ipa.ipatest.lan' 57 | FREEIPA_USERNAME: 'admin' 58 | FREEIPA_PASSWORD: 'P@ssword' 59 | timeout-minutes: 30 60 | -------------------------------------------------------------------------------- /docs/resources/hbac_policy_service_membership.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_hbac_policy_service_membership Resource - freeipa" 3 | description: |- 4 | FreeIPA HBAC policy service membership resource. 5 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 6 | --- 7 | 8 | # freeipa_hbac_policy_service_membership (Resource) 9 | 10 | FreeIPA HBAC policy service membership resource. 11 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 12 | 13 | 14 | ## Example Usage 15 | 16 | ```terraform 17 | resource "freeipa_hbac_policy" "hbac-0" { 18 | name = "test-hbac" 19 | description = "Test HBAC policy" 20 | enabled = true 21 | hostcategory = "all" 22 | servicecategory = "all" 23 | } 24 | 25 | resource "freeipa_hbac_policy_service_membership" "hbac-svc-1" { 26 | name = "test-hbac" 27 | service = "sshd" 28 | } 29 | 30 | resource "freeipa_hbac_policy_service_membership" "hbac-svc-2" { 31 | name = "test-hbac" 32 | services = ["sshd"] 33 | identifier = "hbac-svc-2" 34 | } 35 | 36 | resource "freeipa_hbac_policy_service_membership" "hbac-svcgrp-1" { 37 | name = "test-hbac" 38 | servicegroup = "Sudo" 39 | } 40 | 41 | resource "freeipa_hbac_policy_service_membership" "hbac-svcgrp-2" { 42 | name = "test-hbac" 43 | servicegroups = ["Sudo", "ftp"] 44 | identifier = "hbac-svcgrp-2" 45 | } 46 | ``` 47 | 48 | 49 | 50 | 51 | 52 | ## Schema 53 | 54 | ### Required 55 | 56 | - `name` (String) HBAC policy name 57 | 58 | ### Optional 59 | 60 | - `identifier` (String) Unique identifier to differentiate multiple HBAC policy service membership resources on the same HBAC policy. Manadatory for using services/servicegroups configurations. 61 | - `service` (String, Deprecated) **deprecated** Service name the policy is applied t 62 | - `servicegroup` (String, Deprecated) **deprecated** Service group name the policy is applied to 63 | - `servicegroups` (List of String) List of service group name the policy is applied to 64 | - `services` (List of String) List of service name the policy is applied t 65 | 66 | ### Read-Only 67 | 68 | - `id` (String) ID of the resource 69 | -------------------------------------------------------------------------------- /docs/resources/user_group_membership.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_user_group_membership Resource - freeipa" 3 | description: |- 4 | FreeIPA User Group Membership resource. 5 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 6 | --- 7 | 8 | # freeipa_user_group_membership (Resource) 9 | 10 | FreeIPA User Group Membership resource. 11 | Adding a member that already exist in FreeIPA will result in a warning but the member will be added to the state. 12 | 13 | 14 | ## Example Usage 15 | 16 | ```terraform 17 | resource "freeipa_user_group_membership" "test-0" { 18 | name = "test-group-2" 19 | user = "roman" 20 | } 21 | 22 | resource "freeipa_user_group_membership" "test-1" { 23 | name = "test-group-2" 24 | group = "test-group" 25 | } 26 | 27 | resource "freeipa_user_group_membership" "test-2" { 28 | name = "test-group-2" 29 | external_member = "domain users@adtest.lan" 30 | } 31 | 32 | resource "freeipa_user_group_membership" "test-3" { 33 | name = "test-group-3" 34 | users = ["user1", "user2"] 35 | groups = ["group1", "group2"] 36 | identifier = "my_unique_identifier" 37 | } 38 | ``` 39 | 40 | 41 | 42 | 43 | 44 | ## Schema 45 | 46 | ### Required 47 | 48 | - `name` (String) Group name 49 | 50 | ### Optional 51 | 52 | - `external_member` (String, Deprecated) **deprecated** External member to add. name must refer to an external group. (Requires a valid AD Trust configuration).. Will be replaced by external_members. 53 | - `external_members` (List of String) External members to add as group members. name must refer to an external group. (Requires a valid AD Trust configuration). 54 | - `group` (String, Deprecated) **deprecated** User group to add. Will be replaced by groups. 55 | - `groups` (List of String) User groups to add as group members 56 | - `identifier` (String) Unique identifier to differentiate multiple user group membership resources on the same group. Manadatory for using users/groups/external_members configurations. 57 | - `user` (String, Deprecated) **deprecated** User to add. Will be replaced by users. 58 | - `users` (List of String) Users to add as group members 59 | 60 | ### Read-Only 61 | 62 | - `id` (String) ID of the resource 63 | -------------------------------------------------------------------------------- /docs/data-sources/dns_zone.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_dns_zone Data Source - freeipa" 3 | description: |- 4 | FreeIPA DNS Zone resource 5 | --- 6 | 7 | # freeipa_dns_zone (Data Source) 8 | 9 | FreeIPA DNS Zone resource 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | data "freeipa_dns_zone" "dns-zone-0" { 16 | zone_name = "test.example.lan." 17 | } 18 | 19 | data "freeipa_dns_zone" "dns-zone-1" { 20 | zone_name = "23.168.192.in-addr.arpa." 21 | } 22 | ``` 23 | 24 | 25 | 26 | ## Schema 27 | 28 | ### Required 29 | 30 | - `zone_name` (String) Zone name (FQDN) 31 | 32 | ### Read-Only 33 | 34 | - `admin_email_address` (String) Administrator e-mail address 35 | - `allow_inline_dnssec_signing` (Boolean) Allow inline DNSSEC signing of records in the zone 36 | - `allow_prt_sync` (Boolean) Allow synchronization of forward (A, AAAA) and reverse (PTR) records in the zone 37 | - `allow_query` (String) Semicolon separated list of IP addresses or networks which are allowed to issue queries 38 | - `allow_transfer` (String) Semicolon separated list of IP addresses or networks which are allowed to transfer the zone 39 | - `authoritative_nameserver` (String) Authoritative nameserver domain name 40 | - `bind_update_policy` (String) BIND update policy 41 | - `default_ttl` (Number) Time to live for records without explicit TTL definition 42 | - `disable_zone` (Boolean) Allow disabled the zone 43 | - `dynamic_updates` (Boolean) Allow dynamic updates 44 | - `id` (String) ID of the resource 45 | - `nsec3param_record` (String) NSEC3PARAM record for zone in format: hash_algorithm flags iterations salt 46 | - `skip_nameserver_check` (Boolean) Force DNS zone creation even if nameserver is not resolvable 47 | - `skip_overlap_check` (Boolean) Force DNS zone creation even if it will overlap with an existing zone 48 | - `soa_expire` (Number) SOA record expire time 49 | - `soa_minimum` (Number) How long should negative responses be cached 50 | - `soa_refresh` (Number) SOA record refresh time 51 | - `soa_retry` (Number) SOA record retry time 52 | - `soa_serial_number` (Number) SOA record serial number 53 | - `ttl` (Number) Time to live for records at zone apex 54 | - `zone_forwarders` (List of String) Per-zone forwarders. A custom port can be specified for each forwarder using a standard format IP_ADDRESS port PORT 55 | -------------------------------------------------------------------------------- /tools/go.mod: -------------------------------------------------------------------------------- 1 | module tools 2 | 3 | go 1.22 4 | 5 | require github.com/hashicorp/terraform-plugin-docs v0.19.4 6 | 7 | require ( 8 | github.com/BurntSushi/toml v1.2.1 // indirect 9 | github.com/Kunde21/markdownfmt/v3 v3.1.0 // indirect 10 | github.com/Masterminds/goutils v1.1.1 // indirect 11 | github.com/Masterminds/semver/v3 v3.2.0 // indirect 12 | github.com/Masterminds/sprig/v3 v3.2.3 // indirect 13 | github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect 14 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect 15 | github.com/armon/go-radix v1.0.0 // indirect 16 | github.com/bgentry/speakeasy v0.1.0 // indirect 17 | github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect 18 | github.com/cloudflare/circl v1.3.7 // indirect 19 | github.com/fatih/color v1.16.0 // indirect 20 | github.com/google/uuid v1.3.0 // indirect 21 | github.com/hashicorp/cli v1.1.6 // indirect 22 | github.com/hashicorp/errwrap v1.1.0 // indirect 23 | github.com/hashicorp/go-checkpoint v0.5.0 // indirect 24 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 25 | github.com/hashicorp/go-multierror v1.1.1 // indirect 26 | github.com/hashicorp/go-uuid v1.0.3 // indirect 27 | github.com/hashicorp/go-version v1.7.0 // indirect 28 | github.com/hashicorp/hc-install v0.7.0 // indirect 29 | github.com/hashicorp/terraform-exec v0.21.0 // indirect 30 | github.com/hashicorp/terraform-json v0.22.1 // indirect 31 | github.com/huandu/xstrings v1.3.3 // indirect 32 | github.com/imdario/mergo v0.3.15 // indirect 33 | github.com/mattn/go-colorable v0.1.13 // indirect 34 | github.com/mattn/go-isatty v0.0.20 // indirect 35 | github.com/mattn/go-runewidth v0.0.9 // indirect 36 | github.com/mitchellh/copystructure v1.2.0 // indirect 37 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 38 | github.com/posener/complete v1.2.3 // indirect 39 | github.com/shopspring/decimal v1.3.1 // indirect 40 | github.com/spf13/cast v1.5.0 // indirect 41 | github.com/yuin/goldmark v1.7.1 // indirect 42 | github.com/yuin/goldmark-meta v1.1.0 // indirect 43 | github.com/zclconf/go-cty v1.14.4 // indirect 44 | go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect 45 | golang.org/x/crypto v0.21.0 // indirect 46 | golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect 47 | golang.org/x/mod v0.17.0 // indirect 48 | golang.org/x/sys v0.18.0 // indirect 49 | golang.org/x/text v0.15.0 // indirect 50 | gopkg.in/yaml.v2 v2.3.0 // indirect 51 | gopkg.in/yaml.v3 v3.0.1 // indirect 52 | ) 53 | -------------------------------------------------------------------------------- /docs/data-sources/host.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_host Data Source - freeipa" 3 | description: |- 4 | FreeIPA Host data source 5 | --- 6 | 7 | # freeipa_host (Data Source) 8 | 9 | FreeIPA Host data source 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | data "freeipa_host" "host-0" { 16 | name = "testhost.example.lan" 17 | } 18 | ``` 19 | 20 | 21 | 22 | ## Schema 23 | 24 | ### Required 25 | 26 | - `name` (String) Host fully qualified name 27 | 28 | - May contain only letters, numbers, '-'. 29 | - DNS label may not start or end with '-' 30 | 31 | ### Optional 32 | 33 | - `trusted_for_delegation` (Boolean) Client credentials may be delegated to the service 34 | 35 | ### Read-Only 36 | 37 | - `assigned_idview` (String) Assigned ID View 38 | - `description` (String) A description of this host 39 | - `id` (String) ID of the resource in the terraform state 40 | - `ipasshpubkeys` (List of String) SSH public keys 41 | - `krb_auth_indicators` (List of String) Defines a whitelist for Authentication Indicators. Use 'otp' to allow OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA authentications. Other values may be used for custom configurations. 42 | - `krb_preauth` (Boolean) Pre-authentication is required for the service 43 | - `locality` (String) Host locality (e.g. 'Baltimore, MD') 44 | - `location` (String) Host location (e.g. 'Lab 2') 45 | - `mac_addresses` (List of String) Hardware MAC address(es) on this host 46 | - `memberof_hbacrule` (List of String) List of HBAC rules this user is member of. 47 | - `memberof_hostgroup` (List of String) List of hostgroups this user is member of. 48 | - `memberof_indirect_hbacrule` (List of String) List of HBAC rules this user is indirectly member of. 49 | - `memberof_indirect_hostgroup` (List of String) List of hostgroups this user is is indirectly member of. 50 | - `memberof_indirect_sudorule` (List of String) List of SUDO rules this user is is indirectly member of. 51 | - `memberof_sudorule` (List of String) List of SUDO rules this user is member of. 52 | - `operating_system` (String) Host operating system and version (e.g. 'Fedora 40') 53 | - `platform` (String) Host hardware platform (e.g. 'Lenovo T61') 54 | - `trusted_to_auth_as_delegate` (Boolean) The service is allowed to authenticate on behalf of a client 55 | - `user_certificates` (List of String) Base-64 encoded host certificate 56 | - `userclass` (List of String) Host category (semantics placed on this attribute are for local interpretation) 57 | -------------------------------------------------------------------------------- /freeipa/sudo_cmd_test.go: -------------------------------------------------------------------------------- 1 | // Authors: 2 | // Antoine Gatineau 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-only 5 | 6 | package freeipa 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/hashicorp/terraform-plugin-testing/helper/resource" 12 | "github.com/hashicorp/terraform-plugin-testing/plancheck" 13 | ) 14 | 15 | func TestAccFreeIPASudoCmd_simple(t *testing.T) { 16 | testSudoCmd := map[string]string{ 17 | "index": "1", 18 | "name": "\"/usr/bin/testacc-bash\"", 19 | "description": "\"The bash shell\"", 20 | } 21 | 22 | resource.Test(t, resource.TestCase{ 23 | PreCheck: func() { testAccPreCheck(t) }, 24 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 25 | Steps: []resource.TestStep{ 26 | { 27 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoCmd_resource(testSudoCmd), 28 | Check: resource.ComposeAggregateTestCheckFunc( 29 | resource.TestCheckResourceAttr("freeipa_sudo_cmd.sudocmd-1", "name", "/usr/bin/testacc-bash"), 30 | resource.TestCheckResourceAttr("freeipa_sudo_cmd.sudocmd-1", "description", "The bash shell"), 31 | ), 32 | }, 33 | { 34 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoCmd_resource(testSudoCmd), 35 | ConfigPlanChecks: resource.ConfigPlanChecks{ 36 | PreApply: []plancheck.PlanCheck{ 37 | plancheck.ExpectEmptyPlan(), 38 | }, 39 | }, 40 | }, 41 | }, 42 | }) 43 | } 44 | 45 | func TestAccFreeIPASudoCmd_simple_CaseSensitive(t *testing.T) { 46 | testSudoCmd := map[string]string{ 47 | "index": "1", 48 | "name": "\"/usr/bin/TestACC-Bash\"", 49 | "description": "\"The bash shell\"", 50 | } 51 | 52 | resource.Test(t, resource.TestCase{ 53 | PreCheck: func() { testAccPreCheck(t) }, 54 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 55 | Steps: []resource.TestStep{ 56 | { 57 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoCmd_resource(testSudoCmd), 58 | Check: resource.ComposeAggregateTestCheckFunc( 59 | resource.TestCheckResourceAttr("freeipa_sudo_cmd.sudocmd-1", "name", "/usr/bin/TestACC-Bash"), 60 | resource.TestCheckResourceAttr("freeipa_sudo_cmd.sudocmd-1", "description", "The bash shell"), 61 | ), 62 | }, 63 | { 64 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoCmd_resource(testSudoCmd), 65 | ConfigPlanChecks: resource.ConfigPlanChecks{ 66 | PreApply: []plancheck.PlanCheck{ 67 | plancheck.ExpectEmptyPlan(), 68 | }, 69 | }, 70 | }, 71 | }, 72 | }) 73 | } 74 | -------------------------------------------------------------------------------- /freeipa/sudo_cmdgroup_test.go: -------------------------------------------------------------------------------- 1 | // Authors: 2 | // Antoine Gatineau 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-only 5 | 6 | package freeipa 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/hashicorp/terraform-plugin-testing/helper/resource" 12 | "github.com/hashicorp/terraform-plugin-testing/plancheck" 13 | ) 14 | 15 | func TestAccFreeIPASudoCmdGrp_simple(t *testing.T) { 16 | testSudoCmdGrp := map[string]string{ 17 | "index": "1", 18 | "name": "\"testacc-command-group-1\"", 19 | "description": "\"A set of commands\"", 20 | } 21 | 22 | resource.Test(t, resource.TestCase{ 23 | PreCheck: func() { testAccPreCheck(t) }, 24 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 25 | Steps: []resource.TestStep{ 26 | { 27 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoCmdGrp_resource(testSudoCmdGrp), 28 | Check: resource.ComposeAggregateTestCheckFunc( 29 | resource.TestCheckResourceAttr("freeipa_sudo_cmdgroup.sudocmdgroup-1", "name", "testacc-command-group-1"), 30 | resource.TestCheckResourceAttr("freeipa_sudo_cmdgroup.sudocmdgroup-1", "description", "A set of commands"), 31 | ), 32 | }, 33 | { 34 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoCmdGrp_resource(testSudoCmdGrp), 35 | ConfigPlanChecks: resource.ConfigPlanChecks{ 36 | PreApply: []plancheck.PlanCheck{ 37 | plancheck.ExpectEmptyPlan(), 38 | }, 39 | }, 40 | }, 41 | }, 42 | }) 43 | } 44 | 45 | func TestAccFreeIPASudoCmdGrp_CaseInsensitive(t *testing.T) { 46 | testSudoCmdGrp := map[string]string{ 47 | "index": "1", 48 | "name": "\"Testacc Command Group 1\"", 49 | "description": "\"A set of commands\"", 50 | } 51 | 52 | resource.Test(t, resource.TestCase{ 53 | PreCheck: func() { testAccPreCheck(t) }, 54 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 55 | Steps: []resource.TestStep{ 56 | { 57 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoCmdGrp_resource(testSudoCmdGrp), 58 | Check: resource.ComposeAggregateTestCheckFunc( 59 | resource.TestCheckResourceAttr("freeipa_sudo_cmdgroup.sudocmdgroup-1", "name", "Testacc Command Group 1"), 60 | resource.TestCheckResourceAttr("freeipa_sudo_cmdgroup.sudocmdgroup-1", "description", "A set of commands"), 61 | ), 62 | }, 63 | { 64 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoCmdGrp_resource(testSudoCmdGrp), 65 | ConfigPlanChecks: resource.ConfigPlanChecks{ 66 | PreApply: []plancheck.PlanCheck{ 67 | plancheck.ExpectEmptyPlan(), 68 | }, 69 | }, 70 | }, 71 | }, 72 | }) 73 | } 74 | -------------------------------------------------------------------------------- /docs/resources/dns_record.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_dns_record Resource - freeipa" 3 | description: |- 4 | FreeIPA DNS Record resource 5 | --- 6 | 7 | # freeipa_dns_record (Resource) 8 | 9 | FreeIPA DNS Record resource 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | resource "freeipa_dns_zone" "dns_zone-2" { 16 | zone_name = "test.roman.com.ua." 17 | skip_overlap_check = true 18 | } 19 | 20 | resource "freeipa_dns_record" "record-8" { 21 | zone_name = resource.freeipa_dns_zone.dns_zone-2.id 22 | name = "test-record" 23 | records = ["192.168.10.10", "192.168.10.11"] 24 | type = "A" 25 | } 26 | 27 | resource "freeipa_dns_record" "record-7" { 28 | zone_name = "record.com.ua." 29 | name = "test-record" 30 | records = ["2 1 84DE37B22918F76ED66910B47EB440B0A35F4A56"] 31 | type = "SSHFP" 32 | } 33 | ``` 34 | 35 | 36 | 37 | ## Import Usage 38 | 39 | ```terraform 40 | # import id must be of format ;;; 41 | # or without the optional set_identifier : ;; 42 | 43 | import { 44 | to = freeipa_dns_record.txtrecord 45 | id = "_kerberos;testimport.ipatest.lan;TXT;krbtxt" 46 | } 47 | 48 | resource "freeipa_dns_record" "txtrecord" { 49 | name = "_kerberos" 50 | zone_name = "testimport.ipatest.lan" 51 | type = "TXT" 52 | records = ["IPATEST.LAN"] 53 | set_identifier = "krbtxt" 54 | } 55 | 56 | import { 57 | to = freeipa_dns_record.arecord 58 | id = "test;testimport.ipatest.lan;A" 59 | } 60 | 61 | resource "freeipa_dns_record" "arecord" { 62 | name = "test" 63 | zone_name = "testimport.ipatest.lan" 64 | type = "A" 65 | records = ["172.27.2.2"] 66 | } 67 | 68 | import { 69 | to = freeipa_dns_record.ptrrecord 70 | id = "2;2.27.172.in-addr.arpa;PTR" 71 | } 72 | 73 | resource "freeipa_dns_record" "ptrrecord" { 74 | name = "2" 75 | zone_name = "2.27.172.in-addr.arpa" 76 | type = "PTR" 77 | records = ["test.testimport.ipatest.lan."] 78 | } 79 | ``` 80 | 81 | 82 | 83 | ## Schema 84 | 85 | ### Required 86 | 87 | - `name` (String) Record name 88 | - `records` (Set of String) A string list of records 89 | - `type` (String) The record type (A, AAAA, CNAME, MX, PTR, SRV, TXT, SSHFP, NS) 90 | - `zone_name` (String) Zone name (FQDN) 91 | 92 | ### Optional 93 | 94 | - `set_identifier` (String) Unique identifier to differentiate records with routing policies from one another 95 | - `ttl` (Number) Time to live 96 | 97 | ### Read-Only 98 | 99 | - `id` (String) ID of the resource 100 | -------------------------------------------------------------------------------- /docs/resources/host.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_host Resource - freeipa" 3 | description: |- 4 | FreeIPA Host resource 5 | --- 6 | 7 | # freeipa_host (Resource) 8 | 9 | FreeIPA Host resource 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | resource "freeipa_host" "host-1" { 16 | name = "host-1.example.test" 17 | ip_address = "192.168.1.65" 18 | description = "FreeIPA client in example.test domain" 19 | mac_addresses = ["00:00:00:AA:AA:AA", "00:00:00:BB:BB:BB"] 20 | } 21 | ``` 22 | 23 | 24 | 25 | ## Import Usage 26 | 27 | ```terraform 28 | # The import id must be exactly the same as the name of the host, which must be the fqdn of the host. 29 | 30 | import { 31 | to = freeipa_host.testhost 32 | id = "testhost.ipatest.lan" 33 | } 34 | 35 | resource "freeipa_host" "testhost" { 36 | name = "testhost.ipatest.lan" 37 | } 38 | ``` 39 | 40 | 41 | 42 | ## Schema 43 | 44 | ### Required 45 | 46 | - `name` (String) Host fully qualified name 47 | 48 | - May contain only letters, numbers, '-'. 49 | - DNS label may not start or end with '-' 50 | 51 | ### Optional 52 | 53 | - `assigned_idview` (String) Assigned ID View 54 | - `description` (String) A description of this host 55 | - `force` (Boolean) Skip host's DNS check (A/AAAA) before adding it 56 | - `ip_address` (String) IP address of the host 57 | - `ipasshpubkeys` (List of String) SSH public keys 58 | - `krb_auth_indicators` (List of String) Defines a whitelist for Authentication Indicators. Use 'otp' to allow OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA authentications. Other values may be used for custom configurations. 59 | - `krb_preauth` (Boolean) Pre-authentication is required for the service 60 | - `locality` (String) Host locality (e.g. 'Baltimore, MD') 61 | - `location` (String) Host location (e.g. 'Lab 2') 62 | - `mac_addresses` (List of String) Hardware MAC address(es) on this host 63 | - `operating_system` (String) Host operating system and version (e.g. 'Fedora 40') 64 | - `platform` (String) Host hardware platform (e.g. 'Lenovo T61') 65 | - `random_password` (Boolean) Generate a random password to be used in bulk enrollment 66 | - `trusted_for_delegation` (Boolean) Client credentials may be delegated to the service 67 | - `trusted_to_auth_as_delegate` (Boolean) The service is allowed to authenticate on behalf of a client 68 | - `update_dns` (Boolean) Update DNS when updating or deleting the host (default to `true`) 69 | - `user_certificates` (List of String) Base-64 encoded host certificate 70 | - `userclass` (List of String) Host category (semantics placed on this attribute are for local interpretation) 71 | - `userpassword` (String, Sensitive) Password used in bulk enrollment 72 | 73 | ### Read-Only 74 | 75 | - `generated_password` (String, Sensitive) Generated random password created at host creation 76 | - `id` (String) ID of the resource 77 | -------------------------------------------------------------------------------- /docs/resources/group.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_group Resource - freeipa" 3 | description: |- 4 | FreeIPA User Group resource 5 | --- 6 | 7 | # freeipa_group (Resource) 8 | 9 | FreeIPA User Group resource 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | resource "freeipa_group" "group-posix" { 16 | name = "test-group" 17 | description = "Test group" 18 | gid_number = "12345789" 19 | } 20 | 21 | resource "freeipa_group" "group-nonposix" { 22 | name = "test-group" 23 | description = "Test group" 24 | nonposix = true 25 | } 26 | 27 | resource "freeipa_group" "group-external" { 28 | name = "test-group" 29 | description = "Test group" 30 | external = true 31 | } 32 | ``` 33 | 34 | 35 | 36 | ## Import Usage 37 | 38 | ```terraform 39 | # The import id must be exactly the same as the name of the user group. 40 | 41 | # for posix groups, only the name needs to be defined in the resource statement. 42 | import { 43 | to = freeipa_group.group-posix 44 | id = "testposix" 45 | } 46 | 47 | resource "freeipa_group" "group-posix" { 48 | name = "testposix" 49 | } 50 | 51 | # for external groups, the external attribute must also be defined in the resource statement. 52 | import { 53 | to = freeipa_group.group-external 54 | id = "testexternal" 55 | } 56 | 57 | resource "freeipa_group" "group-external" { 58 | name = "testexternal" 59 | external = true 60 | } 61 | 62 | 63 | # for non posix groups, the nonposix attribute must also be defined in the resource statement. 64 | import { 65 | to = freeipa_group.group-nonposix 66 | id = "testnonposix" 67 | } 68 | 69 | resource "freeipa_group" "group-nonposix" { 70 | name = "testnonposix" 71 | nonposix = true 72 | } 73 | 74 | # note that nonposix and external are two mutually exclusive attributes. 75 | # setting the wrong attributes for the group will result in the replacement of the resource (destroy and recreate) 76 | ``` 77 | 78 | 79 | 80 | ## Schema 81 | 82 | ### Required 83 | 84 | - `name` (String) Group name 85 | 86 | - The name must not exceed 32 characters. 87 | - The name must contain only lowercase letters (a-z), digits (0-9), and the characters (. - _). 88 | - The name must not start with a special character. 89 | - A user and a group cannot have the same name. 90 | 91 | ### Optional 92 | 93 | - `addattr` (List of String) Add an attribute/value pair. Format is attr=value. The attribute must be part of the LDAP schema. 94 | - `description` (String) Group Description 95 | - `external` (Boolean) Allow adding external non-IPA members from trusted domains 96 | - `gid_number` (Number) GID (use this option to set it manually) 97 | - `nonposix` (Boolean) Create as a non-POSIX group 98 | - `setattr` (List of String) Set an attribute to a name/value pair. Format is attr=value. 99 | 100 | ### Read-Only 101 | 102 | - `id` (String) ID of the resource 103 | -------------------------------------------------------------------------------- /docs/resources/dns_zone.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_dns_zone Resource - freeipa" 3 | description: |- 4 | FreeIPA DNS Zone resource 5 | --- 6 | 7 | # freeipa_dns_zone (Resource) 8 | 9 | FreeIPA DNS Zone resource 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | resource "freeipa_dns_zone" "dns_zone-2" { 16 | zone_name = "test.roman.com.ua" 17 | skip_overlap_check = true 18 | disable_zone = false 19 | } 20 | ``` 21 | 22 | 23 | 24 | ## Import Usage 25 | 26 | ```terraform 27 | # forward zone 28 | # The import id attribute must be the undotted fqdn of the zone to import 29 | 30 | import { 31 | to = freeipa_dns_zone.testzone 32 | id = "testimport.ipatest.lan" 33 | } 34 | 35 | resource "freeipa_dns_zone" "testzone" { 36 | zone_name = "testimport.ipatest.lan" 37 | } 38 | 39 | # reverse zone 40 | # The import id attribute must be the undotted fqdn of the zone to import 41 | 42 | import { 43 | to = freeipa_dns_zone.reversetestzone 44 | id = "2.27.172.in-addr.arpa" 45 | } 46 | 47 | resource "freeipa_dns_zone" "reversetestzone" { 48 | zone_name = "2.27.172.in-addr.arpa" 49 | } 50 | 51 | # note that the is_reverse_zone must not be defined, this is only useful for creation 52 | ``` 53 | 54 | 55 | 56 | ## Schema 57 | 58 | ### Required 59 | 60 | - `zone_name` (String) Zone name (FQDN) 61 | 62 | ### Optional 63 | 64 | - `admin_email_address` (String) Administrator e-mail address 65 | - `allow_inline_dnssec_signing` (Boolean) Allow inline DNSSEC signing of records in the zone 66 | - `allow_prt_sync` (Boolean) Allow synchronization of forward (A, AAAA) and reverse (PTR) records in the zone 67 | - `allow_query` (String) Semicolon separated list of IP addresses or networks which are allowed to issue queries 68 | - `allow_transfer` (String) Semicolon separated list of IP addresses or networks which are allowed to transfer the zone 69 | - `authoritative_nameserver` (String) Authoritative nameserver domain name 70 | - `bind_update_policy` (String) BIND update policy 71 | - `default_ttl` (Number) Time to live for records without explicit TTL definition 72 | - `disable_zone` (Boolean) Allow disabled the zone 73 | - `dynamic_updates` (Boolean) Allow dynamic updates 74 | - `is_reverse_zone` (Boolean) Allow create the reverse zone 75 | - `nsec3param_record` (String) NSEC3PARAM record for zone in format: hash_algorithm flags iterations salt 76 | - `skip_nameserver_check` (Boolean) Force DNS zone creation even if nameserver is not resolvable 77 | - `skip_overlap_check` (Boolean) Force DNS zone creation even if it will overlap with an existing zone 78 | - `soa_expire` (Number) SOA record expire time 79 | - `soa_minimum` (Number) How long should negative responses be cached 80 | - `soa_refresh` (Number) SOA record refresh time 81 | - `soa_retry` (Number) SOA record retry time 82 | - `ttl` (Number) Time to live for records at zone apex 83 | - `zone_forwarders` (List of String) Per-zone forwarders. A custom port can be specified for each forwarder using a standard format IP_ADDRESS port PORT 84 | 85 | ### Read-Only 86 | 87 | - `computed_zone_name` (String) Real zone name compatible with ARPA (ie: `domain.tld.`) 88 | - `id` (String) ID of the resource 89 | -------------------------------------------------------------------------------- /docs/data-sources/user.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_user Data Source - freeipa" 3 | description: |- 4 | FreeIPA User data source 5 | --- 6 | 7 | # freeipa_user (Data Source) 8 | 9 | FreeIPA User data source 10 | 11 | 12 | ## Example Usage 13 | 14 | ```terraform 15 | # Lookup an active user 16 | 17 | data "freeipa_user" "user-0" { 18 | name = "test-user" 19 | } 20 | 21 | # Lookup an staged user 22 | 23 | data "freeipa_user" "user-0" { 24 | name = "test-user" 25 | state = "staged" 26 | } 27 | ``` 28 | 29 | 30 | 31 | ## Schema 32 | 33 | ### Required 34 | 35 | - `name` (String) UID or Login 36 | 37 | - The name must not exceed 32 characters. 38 | - The name must contain only lowercase letters (a-z), digits (0-9), and the characters (. - _). 39 | - The name must not start with a special character. 40 | - A user and a group cannot have the same name. 41 | 42 | ### Optional 43 | 44 | - `state` (String) State of the account to lookup. Can be `active`, `disabled`, `staged` or `preserved` 45 | - `user_certificates` (Set of String) List of Base-64 encoded user certificates 46 | 47 | ### Read-Only 48 | 49 | - `account_disabled` (Boolean) Is the account disabled 50 | - `account_preserved` (Boolean) Is the account preserved 51 | - `account_staged` (Boolean) Is the account staged 52 | - `car_license` (List of String) Car Licenses 53 | - `city` (String) City 54 | - `display_name` (String) Display name 55 | - `email_address` (List of String) Email address 56 | - `employee_number` (String) Employee Number 57 | - `employee_type` (String) Employee Type 58 | - `first_name` (String) First name 59 | - `full_name` (String) Full name 60 | - `gecos` (String) GECOS 61 | - `gid_number` (Number) Group ID Number 62 | - `home_directory` (String) Home Directory 63 | - `id` (String) ID of the resource in the terraform state 64 | - `initials` (String) Initials 65 | - `job_title` (String) Job Title 66 | - `krb_password_expiration` (String) User password expiration [RFC3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.8) format (see [RFC3339 time string](https://tools.ietf.org/html/rfc3339#section-5.8) e.g., `YYYY-MM-DDTHH:MM:SSZ`) 67 | - `krb_principal_expiration` (String) Kerberos principal expiration [RFC3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.8) format (see [RFC3339 time string](https://tools.ietf.org/html/rfc3339#section-5.8) e.g., `YYYY-MM-DDTHH:MM:SSZ`) 68 | - `krb_principal_name` (List of String) Principal alias 69 | - `last_name` (String) Last name 70 | - `login_shell` (String) Login Shell 71 | - `manager` (String) Manager 72 | - `memberof_group` (List of String) List of groups this user is member of. 73 | - `memberof_hbacrule` (List of String) List of HBAC rules this user is member of. 74 | - `memberof_indirect_group` (List of String) List of groups this user is is indirectly member of. 75 | - `memberof_indirect_hbacrule` (List of String) List of HBAC rules this user is indirectly member of. 76 | - `memberof_indirect_sudorule` (List of String) List of SUDO rules this user is is indirectly member of. 77 | - `memberof_sudorule` (List of String) List of SUDO rules this user is member of. 78 | - `mobile_numbers` (List of String) Mobile Number 79 | - `organisation_unit` (String) Org. Unit 80 | - `postal_code` (String) Postal code 81 | - `preferred_language` (String) Preferred Language 82 | - `province` (String) Province/State/Country 83 | - `random_password` (Boolean) Generate a random user password 84 | - `ssh_public_key` (List of String) List of SSH public keys 85 | - `street_address` (String) Street address 86 | - `telephone_numbers` (List of String) Telephone Number 87 | - `uid_number` (Number) User ID Number (system will assign one if not provided) 88 | - `userclass` (List of String) User category (semantics placed on this attribute are for local interpretation) 89 | -------------------------------------------------------------------------------- /freeipa/hostgroup_test.go: -------------------------------------------------------------------------------- 1 | // Authors: 2 | // Antoine Gatineau 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-only 5 | 6 | package freeipa 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/hashicorp/terraform-plugin-testing/helper/resource" 12 | "github.com/hashicorp/terraform-plugin-testing/plancheck" 13 | ) 14 | 15 | func TestAccFreeIPAHostgroup_posix(t *testing.T) { 16 | testHostgroup := map[string]string{ 17 | "index": "1", 18 | "name": "\"testacc-group-1\"", 19 | "description": "\"Test hostgroup 1\"", 20 | } 21 | testHostgroupModified := map[string]string{ 22 | "index": "1", 23 | "name": "\"testacc-grouppos-1\"", 24 | "description": "\"Modified description\"", 25 | } 26 | testHostgroupDS := map[string]string{ 27 | "index": "1", 28 | "name": "freeipa_hostgroup.hostgroup-1.name", 29 | } 30 | 31 | resource.Test(t, resource.TestCase{ 32 | PreCheck: func() { testAccPreCheck(t) }, 33 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 34 | Steps: []resource.TestStep{ 35 | { 36 | Config: testAccFreeIPAProvider() + testAccFreeIPAHostGroup_resource(testHostgroup), 37 | Check: resource.ComposeAggregateTestCheckFunc( 38 | resource.TestCheckResourceAttr("freeipa_hostgroup.hostgroup-1", "description", "Test hostgroup 1"), 39 | ), 40 | }, 41 | { 42 | Config: testAccFreeIPAProvider() + testAccFreeIPAHostGroup_resource(testHostgroupModified) + testAccFreeIPAHostGroup_datasource(testHostgroupDS), 43 | Check: resource.ComposeAggregateTestCheckFunc( 44 | resource.TestCheckResourceAttr("freeipa_hostgroup.hostgroup-1", "description", "Modified description"), 45 | resource.TestCheckResourceAttr("data.freeipa_hostgroup.hostgroup-1", "description", "Modified description"), 46 | ), 47 | }, 48 | { 49 | Config: testAccFreeIPAProvider() + testAccFreeIPAHostGroup_resource(testHostgroupModified) + testAccFreeIPAHostGroup_datasource(testHostgroupDS), 50 | ConfigPlanChecks: resource.ConfigPlanChecks{ 51 | PreApply: []plancheck.PlanCheck{ 52 | plancheck.ExpectEmptyPlan(), 53 | }, 54 | }, 55 | }, 56 | }, 57 | }) 58 | } 59 | 60 | func TestAccFreeIPAHostgroup_posix_CaseInsensitive(t *testing.T) { 61 | testHostgroup := map[string]string{ 62 | "index": "1", 63 | "name": "\"TestACC-Group-1\"", 64 | "description": "\"Test hostgroup 1\"", 65 | } 66 | testHostgroupDS := map[string]string{ 67 | "index": "1", 68 | "name": "freeipa_hostgroup.hostgroup-1.name", 69 | } 70 | 71 | resource.Test(t, resource.TestCase{ 72 | PreCheck: func() { testAccPreCheck(t) }, 73 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 74 | Steps: []resource.TestStep{ 75 | { 76 | Config: testAccFreeIPAProvider() + testAccFreeIPAHostGroup_resource(testHostgroup), 77 | Check: resource.ComposeAggregateTestCheckFunc( 78 | resource.TestCheckResourceAttr("freeipa_hostgroup.hostgroup-1", "id", "testacc-group-1"), 79 | resource.TestCheckResourceAttr("freeipa_hostgroup.hostgroup-1", "name", "TestACC-Group-1"), 80 | resource.TestCheckResourceAttr("freeipa_hostgroup.hostgroup-1", "description", "Test hostgroup 1"), 81 | ), 82 | }, 83 | { 84 | Config: testAccFreeIPAProvider() + testAccFreeIPAHostGroup_resource(testHostgroup) + testAccFreeIPAHostGroup_datasource(testHostgroupDS), 85 | Check: resource.ComposeAggregateTestCheckFunc( 86 | resource.TestCheckResourceAttr("freeipa_hostgroup.hostgroup-1", "description", "Test hostgroup 1"), 87 | resource.TestCheckResourceAttr("data.freeipa_hostgroup.hostgroup-1", "name", "TestACC-Group-1"), 88 | resource.TestCheckResourceAttr("data.freeipa_hostgroup.hostgroup-1", "description", "Test hostgroup 1"), 89 | ), 90 | }, 91 | { 92 | Config: testAccFreeIPAProvider() + testAccFreeIPAHostGroup_resource(testHostgroup) + testAccFreeIPAHostGroup_datasource(testHostgroupDS), 93 | ConfigPlanChecks: resource.ConfigPlanChecks{ 94 | PreApply: []plancheck.PlanCheck{ 95 | plancheck.ExpectEmptyPlan(), 96 | }, 97 | }, 98 | }, 99 | }, 100 | }) 101 | } 102 | -------------------------------------------------------------------------------- /freeipa/dns_record_data_source.go: -------------------------------------------------------------------------------- 1 | // This file was originally inspired by the module structure and design patterns 2 | // used in HashiCorp projects, but all code in this file was written from scratch. 3 | // 4 | // Previously licensed under the MPL-2.0. 5 | // This file is now relicensed under the GNU General Public License v3.0 only, 6 | // as permitted by Section 1.10 of the MPL. 7 | // 8 | // Authors: 9 | // Antoine Gatineau 10 | // 11 | // SPDX-License-Identifier: GPL-3.0-only 12 | 13 | package freeipa 14 | 15 | import ( 16 | "context" 17 | "fmt" 18 | 19 | "github.com/hashicorp/terraform-plugin-framework/datasource" 20 | "github.com/hashicorp/terraform-plugin-framework/datasource/schema" 21 | "github.com/hashicorp/terraform-plugin-framework/types" 22 | "github.com/hashicorp/terraform-plugin-log/tflog" 23 | ipa "github.com/infra-monkey/go-freeipa/freeipa" 24 | ) 25 | 26 | // Ensure provider defined types fully satisfy framework interfaces. 27 | var _ datasource.DataSource = &dnsRecordDataSource{} 28 | var _ datasource.DataSourceWithConfigure = &dnsRecordDataSource{} 29 | 30 | func NewDnsRecordDataSource() datasource.DataSource { 31 | return &dnsRecordDataSource{} 32 | } 33 | 34 | // resourceModel defines the resource implementation. 35 | type dnsRecordDataSource struct { 36 | client *ipa.Client 37 | } 38 | 39 | // resourceModelModel describes the resource data model. 40 | type dnsRecordDataSourceModel struct { 41 | Id types.String `tfsdk:"id"` 42 | RecordName types.String `tfsdk:"record_name"` 43 | ZoneName types.String `tfsdk:"zone_name"` 44 | } 45 | 46 | func (r *dnsRecordDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { 47 | resp.TypeName = req.ProviderTypeName + "_dns_record" 48 | } 49 | 50 | func (r *dnsRecordDataSource) ConfigValidators(ctx context.Context) []datasource.ConfigValidator { 51 | return []datasource.ConfigValidator{} 52 | } 53 | 54 | func (r *dnsRecordDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { 55 | resp.Schema = schema.Schema{ 56 | // This description is used by the documentation generator and the language server. 57 | MarkdownDescription: "FreeIPA DNS Record data source", 58 | 59 | Attributes: map[string]schema.Attribute{ 60 | "id": schema.StringAttribute{ 61 | MarkdownDescription: "ID of the resource", 62 | Computed: true, 63 | }, 64 | "record_name": schema.StringAttribute{ 65 | MarkdownDescription: "Record name", 66 | Required: true, 67 | }, 68 | "zone_name": schema.StringAttribute{ 69 | MarkdownDescription: "Zone name (FQDN)", 70 | Required: true, 71 | }, 72 | }, 73 | } 74 | } 75 | 76 | func (r *dnsRecordDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { 77 | // Prevent panic if the provider has not been configured. 78 | if req.ProviderData == nil { 79 | return 80 | } 81 | 82 | client, ok := req.ProviderData.(*ipa.Client) 83 | 84 | if !ok { 85 | resp.Diagnostics.AddError( 86 | "Unexpected Resource Configure Type", 87 | fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), 88 | ) 89 | 90 | return 91 | } 92 | 93 | r.client = client 94 | } 95 | 96 | func (r *dnsRecordDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { 97 | var data dnsRecordDataSourceModel 98 | 99 | // Read Terraform prior state data into the model 100 | resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) 101 | var zone_name interface{} = data.ZoneName.ValueString() 102 | 103 | if resp.Diagnostics.HasError() { 104 | return 105 | } 106 | 107 | structured := true 108 | reqArgs := ipa.DnsrecordShowArgs{ 109 | Idnsname: data.RecordName.ValueString(), 110 | } 111 | optArgs := ipa.DnsrecordShowOptionalArgs{ 112 | Dnszoneidnsname: &zone_name, 113 | Structured: &structured, 114 | } 115 | 116 | res, err := r.client.DnsrecordShow(&reqArgs, &optArgs) 117 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Read freeipa dns record %s: %s", data.RecordName.ValueString(), res.String())) 118 | if err != nil { 119 | resp.Diagnostics.AddError("Client Error", err.Error()) 120 | return 121 | } 122 | if res != nil { 123 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Read freeipa dns zone: %s", res.Result.String())) 124 | } else { 125 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error reading freeipa dns zone %s", data.RecordName.ValueString())) 126 | return 127 | } 128 | 129 | // Save updated data into Terraform state 130 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 131 | if resp.Diagnostics.HasError() { 132 | return 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/rework-space-com/terraform-provider-freeipa 2 | 3 | go 1.22.7 4 | 5 | toolchain go1.23.4 6 | 7 | require ( 8 | github.com/hashicorp/terraform-plugin-framework v1.13.0 9 | github.com/hashicorp/terraform-plugin-framework-validators v0.16.0 10 | github.com/hashicorp/terraform-plugin-go v0.25.0 11 | github.com/hashicorp/terraform-plugin-log v0.9.0 12 | github.com/hashicorp/terraform-plugin-testing v1.11.0 13 | github.com/infra-monkey/go-freeipa v1.2.3 14 | golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df 15 | ) 16 | 17 | require ( 18 | github.com/BurntSushi/toml v1.2.1 // indirect 19 | github.com/Kunde21/markdownfmt/v3 v3.1.0 // indirect 20 | github.com/Masterminds/goutils v1.1.1 // indirect 21 | github.com/Masterminds/semver/v3 v3.2.0 // indirect 22 | github.com/Masterminds/sprig/v3 v3.2.3 // indirect 23 | github.com/ProtonMail/go-crypto v1.1.3 // indirect 24 | github.com/agext/levenshtein v1.2.2 // indirect 25 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect 26 | github.com/armon/go-radix v1.0.0 // indirect 27 | github.com/bgentry/speakeasy v0.1.0 // indirect 28 | github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect 29 | github.com/cloudflare/circl v1.3.7 // indirect 30 | github.com/fatih/color v1.16.0 // indirect 31 | github.com/golang/protobuf v1.5.4 // indirect 32 | github.com/google/go-cmp v0.6.0 // indirect 33 | github.com/google/uuid v1.6.0 // indirect 34 | github.com/hashicorp/cli v1.1.6 // indirect 35 | github.com/hashicorp/errwrap v1.1.0 // indirect 36 | github.com/hashicorp/go-checkpoint v0.5.0 // indirect 37 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 38 | github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect 39 | github.com/hashicorp/go-hclog v1.6.3 // indirect 40 | github.com/hashicorp/go-multierror v1.1.1 // indirect 41 | github.com/hashicorp/go-plugin v1.6.2 // indirect 42 | github.com/hashicorp/go-retryablehttp v0.7.7 // indirect 43 | github.com/hashicorp/go-uuid v1.0.3 // indirect 44 | github.com/hashicorp/go-version v1.7.0 // indirect 45 | github.com/hashicorp/hc-install v0.9.1 // indirect 46 | github.com/hashicorp/hcl/v2 v2.23.0 // indirect 47 | github.com/hashicorp/logutils v1.0.0 // indirect 48 | github.com/hashicorp/terraform-exec v0.22.0 // indirect 49 | github.com/hashicorp/terraform-json v0.24.0 // indirect 50 | github.com/hashicorp/terraform-plugin-docs v0.19.4 // indirect 51 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.0 // indirect 52 | github.com/hashicorp/terraform-registry-address v0.2.3 // indirect 53 | github.com/hashicorp/terraform-svchost v0.1.1 // indirect 54 | github.com/hashicorp/yamux v0.1.1 // indirect 55 | github.com/huandu/xstrings v1.3.3 // indirect 56 | github.com/imdario/mergo v0.3.15 // indirect 57 | github.com/jcmturner/aescts/v2 v2.0.0 // indirect 58 | github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect 59 | github.com/jcmturner/gofork v1.7.6 // indirect 60 | github.com/jcmturner/goidentity/v6 v6.0.1 // indirect 61 | github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect 62 | github.com/jcmturner/rpc/v2 v2.0.3 // indirect 63 | github.com/kr/pretty v0.3.0 // indirect 64 | github.com/mattn/go-colorable v0.1.14 // indirect 65 | github.com/mattn/go-isatty v0.0.20 // indirect 66 | github.com/mattn/go-runewidth v0.0.9 // indirect 67 | github.com/mitchellh/copystructure v1.2.0 // indirect 68 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect 69 | github.com/mitchellh/go-wordwrap v1.0.0 // indirect 70 | github.com/mitchellh/mapstructure v1.5.0 // indirect 71 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 72 | github.com/oklog/run v1.0.0 // indirect 73 | github.com/pkg/errors v0.9.1 // indirect 74 | github.com/posener/complete v1.2.3 // indirect 75 | github.com/rogpeppe/go-internal v1.13.1 // indirect 76 | github.com/shopspring/decimal v1.3.1 // indirect 77 | github.com/spf13/cast v1.5.0 // indirect 78 | github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect 79 | github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect 80 | github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect 81 | github.com/yuin/goldmark v1.7.1 // indirect 82 | github.com/yuin/goldmark-meta v1.1.0 // indirect 83 | github.com/zclconf/go-cty v1.16.2 // indirect 84 | go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect 85 | golang.org/x/crypto v0.32.0 // indirect 86 | golang.org/x/mod v0.22.0 // indirect 87 | golang.org/x/net v0.33.0 // indirect 88 | golang.org/x/sync v0.11.0 // indirect 89 | golang.org/x/sys v0.29.0 // indirect 90 | golang.org/x/text v0.22.0 // indirect 91 | golang.org/x/tools v0.22.0 // indirect 92 | google.golang.org/appengine v1.6.8 // indirect 93 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect 94 | google.golang.org/grpc v1.67.1 // indirect 95 | google.golang.org/protobuf v1.35.1 // indirect 96 | gopkg.in/yaml.v2 v2.3.0 // indirect 97 | gopkg.in/yaml.v3 v3.0.1 // indirect 98 | ) 99 | -------------------------------------------------------------------------------- /freeipa/dns_zone_test.go: -------------------------------------------------------------------------------- 1 | // Authors: 2 | // Antoine Gatineau 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-only 5 | 6 | package freeipa 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/hashicorp/terraform-plugin-testing/helper/resource" 12 | "github.com/hashicorp/terraform-plugin-testing/plancheck" 13 | ) 14 | 15 | func TestAccFreeIPADNSZone_basic(t *testing.T) { 16 | testZone := map[string]string{ 17 | "index": "0", 18 | "zone_name": "\"ipa.example.lan\"", 19 | } 20 | testZoneModified := map[string]string{ 21 | "index": "0", 22 | "zone_name": "\"ipa.example.lan.\"", 23 | "disable_zone": "true", 24 | } 25 | 26 | resource.Test(t, resource.TestCase{ 27 | PreCheck: func() { testAccPreCheck(t) }, 28 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 29 | Steps: []resource.TestStep{ 30 | { 31 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone), 32 | Check: resource.ComposeAggregateTestCheckFunc( 33 | resource.TestCheckResourceAttr("freeipa_dns_zone.dns-zone-0", "computed_zone_name", "ipa.example.lan."), 34 | ), 35 | }, 36 | { 37 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone), 38 | ConfigPlanChecks: resource.ConfigPlanChecks{ 39 | PreApply: []plancheck.PlanCheck{ 40 | plancheck.ExpectEmptyPlan(), 41 | }, 42 | }, 43 | }, 44 | { 45 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZoneModified), 46 | Check: resource.ComposeAggregateTestCheckFunc( 47 | resource.TestCheckResourceAttr("freeipa_dns_zone.dns-zone-0", "computed_zone_name", "ipa.example.lan."), 48 | ), 49 | }, 50 | { 51 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZoneModified), 52 | ConfigPlanChecks: resource.ConfigPlanChecks{ 53 | PreApply: []plancheck.PlanCheck{ 54 | plancheck.ExpectEmptyPlan(), 55 | }, 56 | }, 57 | }, 58 | }, 59 | }) 60 | } 61 | 62 | func TestAccFreeIPADNSZone_basic_CaseInsensitive(t *testing.T) { 63 | testZone := map[string]string{ 64 | "index": "0", 65 | "zone_name": "\"IPA.example.lan\"", 66 | } 67 | testZoneDS := map[string]string{ 68 | "index": "0", 69 | "zone_name": "\"IPA.example.lan.\"", 70 | } 71 | 72 | resource.Test(t, resource.TestCase{ 73 | PreCheck: func() { testAccPreCheck(t) }, 74 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 75 | Steps: []resource.TestStep{ 76 | { 77 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone), 78 | Check: resource.ComposeAggregateTestCheckFunc( 79 | resource.TestCheckResourceAttr("freeipa_dns_zone.dns-zone-0", "computed_zone_name", "ipa.example.lan."), 80 | ), 81 | }, 82 | { 83 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone), 84 | ConfigPlanChecks: resource.ConfigPlanChecks{ 85 | PreApply: []plancheck.PlanCheck{ 86 | plancheck.ExpectEmptyPlan(), 87 | }, 88 | }, 89 | }, 90 | { 91 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone) + testAccFreeIPADNSZone_datasource(testZoneDS), 92 | Check: resource.ComposeAggregateTestCheckFunc( 93 | resource.TestCheckResourceAttr("data.freeipa_dns_zone.dns-zone-0", "zone_name", "ipa.example.lan."), 94 | ), 95 | }, 96 | }, 97 | }) 98 | } 99 | 100 | func TestAccFreeIPADNSZone_reverse(t *testing.T) { 101 | testZone := map[string]string{ 102 | "index": "0", 103 | "zone_name": "\"192.168.23.0\"", 104 | "is_reverse_zone": "true", 105 | } 106 | testDS := map[string]string{ 107 | "index": "0", 108 | "zone_name": "\"23.168.192.in-addr.arpa.\"", 109 | } 110 | 111 | resource.Test(t, resource.TestCase{ 112 | PreCheck: func() { testAccPreCheck(t) }, 113 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 114 | Steps: []resource.TestStep{ 115 | { 116 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone), 117 | Check: resource.ComposeAggregateTestCheckFunc( 118 | resource.TestCheckResourceAttr("freeipa_dns_zone.dns-zone-0", "computed_zone_name", "23.168.192.in-addr.arpa."), 119 | ), 120 | }, 121 | { 122 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone), 123 | ConfigPlanChecks: resource.ConfigPlanChecks{ 124 | PreApply: []plancheck.PlanCheck{ 125 | plancheck.ExpectEmptyPlan(), 126 | }, 127 | }, 128 | }, 129 | { 130 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone) + testAccFreeIPADNSZone_datasource(testDS), 131 | Check: resource.ComposeAggregateTestCheckFunc( 132 | resource.TestCheckResourceAttr("freeipa_dns_zone.dns-zone-0", "computed_zone_name", "23.168.192.in-addr.arpa."), 133 | ), 134 | }, 135 | }, 136 | }) 137 | } 138 | -------------------------------------------------------------------------------- /freeipa/sudo_rule_option_test.go: -------------------------------------------------------------------------------- 1 | // Authors: 2 | // Antoine Gatineau 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-only 5 | 6 | package freeipa 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/hashicorp/terraform-plugin-testing/helper/resource" 12 | "github.com/hashicorp/terraform-plugin-testing/plancheck" 13 | ) 14 | 15 | func TestAccFreeIPASudoRuleOption_simple(t *testing.T) { 16 | testSudoRule := map[string]string{ 17 | "index": "1", 18 | "name": "\"testacc-sudorule\"", 19 | "description": "\"A sudo rule for acceptance tests\"", 20 | } 21 | testSudoRuleOption := map[string]string{ 22 | "index": "1", 23 | "name": "freeipa_sudo_rule.sudorule-1.name", 24 | "option": "\"!authenticate\"", 25 | } 26 | testSudoDS := map[string]string{ 27 | "index": "1", 28 | "name": "freeipa_sudo_rule.sudorule-1.name", 29 | } 30 | 31 | resource.Test(t, resource.TestCase{ 32 | PreCheck: func() { testAccPreCheck(t) }, 33 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 34 | Steps: []resource.TestStep{ 35 | { 36 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoRule_resource(testSudoRule) + testAccFreeIPASudoRuleOption_resource(testSudoRuleOption), 37 | Check: resource.ComposeAggregateTestCheckFunc( 38 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "name", "testacc-sudorule"), 39 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "description", "A sudo rule for acceptance tests"), 40 | resource.TestCheckResourceAttr("freeipa_sudo_rule_option.sudorule-option-1", "option", "!authenticate"), 41 | ), 42 | }, 43 | { 44 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoRule_resource(testSudoRule) + testAccFreeIPASudoRuleOption_resource(testSudoRuleOption) + testAccFreeIPASudoRule_datasource(testSudoDS), 45 | Check: resource.ComposeAggregateTestCheckFunc( 46 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "name", "testacc-sudorule"), 47 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "option.#", "1"), 48 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "option.0", "!authenticate"), 49 | ), 50 | }, 51 | { 52 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoRule_resource(testSudoRule) + testAccFreeIPASudoRuleOption_resource(testSudoRuleOption) + testAccFreeIPASudoRule_datasource(testSudoDS), 53 | ConfigPlanChecks: resource.ConfigPlanChecks{ 54 | PreApply: []plancheck.PlanCheck{ 55 | plancheck.ExpectEmptyPlan(), 56 | }, 57 | }, 58 | }, 59 | }, 60 | }) 61 | } 62 | 63 | func TestAccFreeIPASudoRuleOption_simple_CaseInsensitive(t *testing.T) { 64 | testSudoRule := map[string]string{ 65 | "index": "1", 66 | "name": "\"TestACC-SudoRule\"", 67 | "description": "\"A sudo rule for acceptance tests\"", 68 | } 69 | testSudoRuleOption := map[string]string{ 70 | "index": "1", 71 | "name": "freeipa_sudo_rule.sudorule-1.name", 72 | "option": "\"!authenticate\"", 73 | } 74 | testSudoDS := map[string]string{ 75 | "index": "1", 76 | "name": "freeipa_sudo_rule.sudorule-1.name", 77 | } 78 | 79 | resource.Test(t, resource.TestCase{ 80 | PreCheck: func() { testAccPreCheck(t) }, 81 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 82 | Steps: []resource.TestStep{ 83 | { 84 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoRule_resource(testSudoRule) + testAccFreeIPASudoRuleOption_resource(testSudoRuleOption), 85 | Check: resource.ComposeAggregateTestCheckFunc( 86 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "name", "TestACC-SudoRule"), 87 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "description", "A sudo rule for acceptance tests"), 88 | resource.TestCheckResourceAttr("freeipa_sudo_rule_option.sudorule-option-1", "option", "!authenticate"), 89 | ), 90 | }, 91 | { 92 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoRule_resource(testSudoRule) + testAccFreeIPASudoRuleOption_resource(testSudoRuleOption) + testAccFreeIPASudoRule_datasource(testSudoDS), 93 | Check: resource.ComposeAggregateTestCheckFunc( 94 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "name", "TestACC-SudoRule"), 95 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "option.#", "1"), 96 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "option.0", "!authenticate"), 97 | ), 98 | }, 99 | { 100 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoRule_resource(testSudoRule) + testAccFreeIPASudoRuleOption_resource(testSudoRuleOption) + testAccFreeIPASudoRule_datasource(testSudoDS), 101 | ConfigPlanChecks: resource.ConfigPlanChecks{ 102 | PreApply: []plancheck.PlanCheck{ 103 | plancheck.ExpectEmptyPlan(), 104 | }, 105 | }, 106 | }, 107 | }, 108 | }) 109 | } 110 | -------------------------------------------------------------------------------- /_about/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Developing the provider 2 | 3 | Thank you for your interest in contributing. 4 | 5 | ## Documentation 6 | 7 | Terraform [provider development documentation](https://www.terraform.io/docs/extend/) provides a good start into developing an understanding of provider development. 8 | 9 | 10 | ## Building the provider 11 | 12 | There is a [makefile](../GNUmakefile) to help build the provider. You can build the provider by running `make build`. 13 | 14 | ```shell 15 | $ make build 16 | ``` 17 | 18 | ### Tests 19 | To run the full suite of Acceptance tests export the required environment variables and run the `make testacc`. 20 | 21 | 22 | ```shell 23 | $ export FREEIPA_HOST=ipa.ipatest.lan 24 | $ export FREEIPA_USERNAME=admin 25 | $ export FREEIPA_PASSWORD=P@ssword 26 | $ make testacc 27 | ``` 28 | 29 | ## Install provider locally 30 | You can install provider locally for development and testing. 31 | Use the `make install` command to compile the provider into a binary and install it in your `GOBIN` path. 32 | ```shell 33 | $ make install 34 | ``` 35 | 36 | Terraform allows you to use local provider builds by setting a `dev_overrides` block in a configuration file called `.terraformrc`. This block overrides all other configured installation methods. 37 | 38 | Create a new file called `.terraformrc` in your home directory (`~`), then add the `dev_overrides` block below. Change the `` to the value returned from the `go env GOBIN` command. 39 | 40 | If the `GOBIN` go environment variable is not set, use the default path, `/home//go/bin`. 41 | 42 | ```terraform 43 | provider_installation { 44 | 45 | dev_overrides { 46 | "hashicorp.com/edu/freeipa" = "" 47 | } 48 | 49 | # For all other providers, install them directly from their origin provider 50 | # registries as normal. If you omit this, Terraform will _only_ use 51 | # the dev_overrides block, and so no other providers will be available. 52 | direct {} 53 | } 54 | 55 | ``` 56 | 57 | ### Locally installed provider configuration 58 | Use the folloving provider configration block for the locally installed provider 59 | ```terraform 60 | terraform { 61 | required_providers { 62 | freeipa = { 63 | source = "hashicorp.com/edu/freeipa" 64 | } 65 | } 66 | } 67 | 68 | provider "freeipa" { 69 | host = "ipa.ipatest.lan" 70 | username = "admin" 71 | password = "P@ssword" 72 | insecure = true 73 | } 74 | ``` 75 | 76 | ### Create documentation 77 | 78 | When creating or updating resources/data resources please make sure to update the examples in the respective folder (`./examples/resources/` for resources, `./examples/data-sources/` for data sources) 79 | 80 | Next you can use the following command to generate the terraform documentation from go files 81 | 82 | ```shell 83 | make doc 84 | ``` 85 | 86 | ### Start FreeIPA locally 87 | You can start local FreeIPA instance using the provided Docker Compose file. 88 | 89 | Add a record for the FreeIPA test server to the `/etc/hosts`. 90 | ```shell 91 | $ sudo echo "127.0.0.1 ipa.ipatest.lan" | sudo tee -a /etc/hosts 92 | ``` 93 | 94 | In another terminal window, navigate to the `docker_compose` directory. 95 | ```bash 96 | $ cd docker_compose 97 | ``` 98 | 99 | Run `docker-compose up` to spin up a local instance of FreeIPA. 100 | ```bash 101 | $ docker-compose up 102 | ``` 103 | 104 | Leave this process running in your terminal window. Wait until the FreeIPA server configuration is finished. In the original terminal window, verify that the FreeIPA server is running by sending the following requests: 105 | 106 | ```bash 107 | $ curl --insecure -s -c ./cookie.txt -k -H "Content-Type: application/x-www-form-urlencoded" -H "Accept: text/plain" -d "user=admin&password=P@ssword" https://ipa.ipatest.lan/ipa/session/login_password 108 | $ curl --insecure -k -b ./cookie.txt -H "Referer: https://ipa.ipatest.lan/ipa" -H "Content-Type:application/json" https://ipa.ipatest.lan/ipa/json --data '{"method":"env","params":[["version"],{}],"id":0}' 109 | ``` 110 | You should get a response from the FreeIPA API server that contains the server version and information about the principal used for authentication: 111 | ```shell 112 | {"result": {"result": {"version": "4.10.1"}, "count": 1, "total": 120, "summary": null, "messages": [{"type": "warning", "name": "VersionMissing", "message": "API Version number was not sent, forward compatibility not guaranteed. Assuming server's API version, 2.251", "code": 13001, "data": {"server_version": "2.251"}}]}, "error": null, "id": 0, "principal": "admin@IPATEST.LAN", "version": "4.10.1"} 113 | ``` 114 | ### Run individual acceptance test 115 | Once the environment is set, you can run a specific test with this command (example for `TestAccFreeIPASudoRuleAllowCmdMembership_CaseInsensitive`) 116 | 117 | ```shell 118 | TF_ACC=1 /usr/local/go/bin/go test -timeout 30s -run ^TestAccFreeIPASudoRuleAllowCmdMembership_CaseInsensitive$ github.com/rework-space-com/terraform-provider-freeipa/freeipa 119 | ``` 120 | -------------------------------------------------------------------------------- /freeipa/sudo_cmdgroup_data_source.go: -------------------------------------------------------------------------------- 1 | // This file was originally inspired by the module structure and design patterns 2 | // used in HashiCorp projects, but all code in this file was written from scratch. 3 | // 4 | // Previously licensed under the MPL-2.0. 5 | // This file is now relicensed under the GNU General Public License v3.0 only, 6 | // as permitted by Section 1.10 of the MPL. 7 | // 8 | // Authors: 9 | // Antoine Gatineau 10 | // 11 | // SPDX-License-Identifier: GPL-3.0-only 12 | 13 | package freeipa 14 | 15 | import ( 16 | "context" 17 | "fmt" 18 | 19 | "github.com/hashicorp/terraform-plugin-framework/datasource" 20 | "github.com/hashicorp/terraform-plugin-framework/datasource/schema" 21 | "github.com/hashicorp/terraform-plugin-framework/types" 22 | "github.com/hashicorp/terraform-plugin-log/tflog" 23 | ipa "github.com/infra-monkey/go-freeipa/freeipa" 24 | ) 25 | 26 | // Ensure provider defined types fully satisfy framework interfaces. 27 | var _ datasource.DataSource = &SudoCmdGroupDataSource{} 28 | var _ datasource.DataSourceWithConfigure = &SudoCmdGroupDataSource{} 29 | 30 | func NewSudoCmdGroupDataSource() datasource.DataSource { 31 | return &SudoCmdGroupDataSource{} 32 | } 33 | 34 | // SudoCmdGroupDataSource defines the resource implementation. 35 | type SudoCmdGroupDataSource struct { 36 | client *ipa.Client 37 | } 38 | 39 | // UserResourceModel describes the resource data model. 40 | type SudoCmdGroupDataSourceModel struct { 41 | Id types.String `tfsdk:"id"` 42 | Name types.String `tfsdk:"name"` 43 | Description types.String `tfsdk:"description"` 44 | MemberSudocmd types.List `tfsdk:"member_sudocmd"` 45 | } 46 | 47 | func (r *SudoCmdGroupDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { 48 | resp.TypeName = req.ProviderTypeName + "_sudo_cmdgroup" 49 | } 50 | 51 | func (r *SudoCmdGroupDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { 52 | resp.Schema = schema.Schema{ 53 | // This description is used by the documentation generator and the language server. 54 | MarkdownDescription: "FreeIPA Sudo command group data source", 55 | 56 | Attributes: map[string]schema.Attribute{ 57 | "id": schema.StringAttribute{ 58 | MarkdownDescription: "ID of the resource in the terraform state", 59 | Computed: true, 60 | }, 61 | "name": schema.StringAttribute{ 62 | MarkdownDescription: "Name of the sudo command group", 63 | Required: true, 64 | }, 65 | "description": schema.StringAttribute{ 66 | MarkdownDescription: "Description of the sudo command group", 67 | Computed: true, 68 | }, 69 | "member_sudocmd": schema.ListAttribute{ 70 | MarkdownDescription: "List of sudo commands that are member of the sudo command group", 71 | Computed: true, 72 | ElementType: types.StringType, 73 | }, 74 | }, 75 | } 76 | } 77 | 78 | func (r *SudoCmdGroupDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { 79 | // Prevent panic if the provider has not been configured. 80 | if req.ProviderData == nil { 81 | return 82 | } 83 | 84 | client, ok := req.ProviderData.(*ipa.Client) 85 | 86 | if !ok { 87 | resp.Diagnostics.AddError( 88 | "Unexpected Resource Configure Type", 89 | fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), 90 | ) 91 | 92 | return 93 | } 94 | 95 | r.client = client 96 | } 97 | 98 | func (r *SudoCmdGroupDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { 99 | var data SudoCmdGroupDataSourceModel 100 | 101 | // Read Terraform configuration data into the model 102 | resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) 103 | 104 | if resp.Diagnostics.HasError() { 105 | return 106 | } 107 | 108 | all := true 109 | args := ipa.SudocmdgroupShowArgs{ 110 | Cn: data.Name.ValueString(), 111 | } 112 | optArgs := ipa.SudocmdgroupShowOptionalArgs{ 113 | All: &all, 114 | } 115 | 116 | res, err := r.client.SudocmdgroupShow(&args, &optArgs) 117 | if err != nil { 118 | resp.Diagnostics.AddError("Client Error", err.Error()) 119 | return 120 | } 121 | if res != nil { 122 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Read freeipa sudo command group %s", res.Result.String())) 123 | } else { 124 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error reading freeipa sudo command group %s", data.Name.ValueString())) 125 | return 126 | } 127 | 128 | if res.Result.Description != nil { 129 | data.Description = types.StringValue(*res.Result.Description) 130 | } 131 | if res.Result.MemberSudocmd != nil { 132 | data.MemberSudocmd, _ = types.ListValueFrom(ctx, types.StringType, res.Result.MemberSudocmd) 133 | } 134 | data.Id = types.StringValue(res.Result.Cn) 135 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Read freeipa sudo command group %s", res.Result.Cn)) 136 | 137 | // Save updated data into Terraform state 138 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 139 | if resp.Diagnostics.HasError() { 140 | return 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /resource.template: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package freeipa 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | "strconv" 10 | "strings" 11 | 12 | ipa "github.com/infra-monkey/go-freeipa/freeipa" 13 | "github.com/hashicorp/terraform-plugin-framework-validators/resourcevalidator" 14 | "github.com/hashicorp/terraform-plugin-framework/path" 15 | "github.com/hashicorp/terraform-plugin-framework/resource" 16 | "github.com/hashicorp/terraform-plugin-framework/resource/schema" 17 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" 18 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" 19 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" 20 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" 21 | "github.com/hashicorp/terraform-plugin-framework/types" 22 | "github.com/hashicorp/terraform-plugin-log/tflog" 23 | ) 24 | 25 | // Ensure provider defined types fully satisfy framework interfaces. 26 | var _ resource.Resource = &resourceModel{} 27 | var _ resource.ResourceWithImportState = &resourceModel{} 28 | 29 | func NewresourceModel() resource.Resource { 30 | return &resourceModel{} 31 | } 32 | 33 | // resourceModel defines the resource implementation. 34 | type resourceModel struct { 35 | client *ipa.Client 36 | } 37 | 38 | // resourceModelModel describes the resource data model. 39 | type resourceModelModel struct { 40 | Id types.String `tfsdk:"id"` 41 | } 42 | 43 | func (r *resourceModel) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { 44 | resp.TypeName = req.ProviderTypeName + "_group" 45 | } 46 | 47 | func (r *resourceModel) ConfigValidators(ctx context.Context) []resource.ConfigValidator { 48 | return []resource.ConfigValidator{ 49 | } 50 | } 51 | 52 | func (r *resourceModel) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { 53 | resp.Schema = schema.Schema{ 54 | // This description is used by the documentation generator and the language server. 55 | MarkdownDescription: "FreeIPA resource", 56 | 57 | Attributes: map[string]schema.Attribute{ 58 | "id": schema.StringAttribute{ 59 | MarkdownDescription: "ID of the resource", 60 | Computed: true, 61 | PlanModifiers: []planmodifier.String{ 62 | stringplanmodifier.UseStateForUnknown(), 63 | }, 64 | }, 65 | }, 66 | } 67 | } 68 | 69 | func (r *resourceModel) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { 70 | // Prevent panic if the provider has not been configured. 71 | if req.ProviderData == nil { 72 | return 73 | } 74 | 75 | client, ok := req.ProviderData.(*ipa.Client) 76 | 77 | if !ok { 78 | resp.Diagnostics.AddError( 79 | "Unexpected Resource Configure Type", 80 | fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), 81 | ) 82 | 83 | return 84 | } 85 | 86 | r.client = client 87 | } 88 | 89 | func (r *resourceModel) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { 90 | var data resourceModelModel 91 | 92 | // Read Terraform plan data into the model 93 | resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) 94 | 95 | if resp.Diagnostics.HasError() { 96 | return 97 | } 98 | 99 | tflog.Trace(ctx, "created a resource") 100 | 101 | data.Id = types.StringValue(data.Name.ValueString()) 102 | // Save data into Terraform state 103 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 104 | } 105 | 106 | func (r *resourceModel) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { 107 | var data resourceModelModel 108 | 109 | // Read Terraform prior state data into the model 110 | resp.Diagnostics.Append(req.State.Get(ctx, &data)...) 111 | 112 | if resp.Diagnostics.HasError() { 113 | return 114 | } 115 | 116 | // Save updated data into Terraform state 117 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 118 | if resp.Diagnostics.HasError() { 119 | return 120 | } 121 | } 122 | 123 | func (r *resourceModel) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { 124 | var data resourceModelModel 125 | 126 | // Read Terraform plan data into the model 127 | resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) 128 | 129 | if resp.Diagnostics.HasError() { 130 | return 131 | } 132 | 133 | // If applicable, this is a great opportunity to initialize any necessary 134 | // provider client data and make a call using it. 135 | // httpResp, err := r.client.Do(httpReq) 136 | // if err != nil { 137 | // resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update example, got error: %s", err)) 138 | // return 139 | // } 140 | 141 | // Save updated data into Terraform state 142 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 143 | } 144 | 145 | func (r *resourceModel) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { 146 | var data resourceModelModel 147 | 148 | // Read Terraform prior state data into the model 149 | resp.Diagnostics.Append(req.State.Get(ctx, &data)...) 150 | 151 | if resp.Diagnostics.HasError() { 152 | return 153 | } 154 | 155 | // If applicable, this is a great opportunity to initialize any necessary 156 | // provider client data and make a call using it. 157 | // httpResp, err := r.client.Do(httpReq) 158 | // if err != nil { 159 | // resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete example, got error: %s", err)) 160 | // return 161 | // } 162 | } 163 | 164 | func (r *resourceModel) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { 165 | resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) 166 | } 167 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual 11 | identity and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the overall 27 | community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or advances of 32 | any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email address, 36 | without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official email address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | [Contact Us](mailto:sales+github@rework-space.com). 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series of 87 | actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or permanent 94 | ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within the 114 | community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.1, available at 120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 127 | [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | -------------------------------------------------------------------------------- /freeipa/dns_record_test.go: -------------------------------------------------------------------------------- 1 | // Authors: 2 | // Antoine Gatineau 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-only 5 | 6 | package freeipa 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/hashicorp/terraform-plugin-testing/helper/resource" 12 | "github.com/hashicorp/terraform-plugin-testing/plancheck" 13 | ) 14 | 15 | func TestAccFreeIPADNSRecord_A(t *testing.T) { 16 | testZone := map[string]string{ 17 | "index": "0", 18 | "zone_name": "\"ipa.example.lan\"", 19 | } 20 | testRecord := map[string]string{ 21 | "index": "0", 22 | "zone_name": "resource.freeipa_dns_zone.dns-zone-0.id", 23 | "type": "\"A\"", 24 | "name": "\"test-record\"", 25 | "records": "[\"192.168.10.10\", \"192.168.10.11\"]", 26 | } 27 | testRecordMisordered := map[string]string{ 28 | "index": "0", 29 | "zone_name": "resource.freeipa_dns_zone.dns-zone-0.id", 30 | "type": "\"A\"", 31 | "name": "\"test-record\"", 32 | "records": "[\"192.168.10.11\", \"192.168.10.10\"]", 33 | } 34 | 35 | resource.Test(t, resource.TestCase{ 36 | PreCheck: func() { testAccPreCheck(t) }, 37 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 38 | Steps: []resource.TestStep{ 39 | { 40 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone) + testAccFreeIPADNSRecord_resource(testRecord), 41 | Check: resource.ComposeAggregateTestCheckFunc( 42 | resource.TestCheckResourceAttr("freeipa_dns_record.dns-record-0", "name", "test-record"), 43 | ), 44 | }, 45 | { 46 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone) + testAccFreeIPADNSRecord_resource(testRecord), 47 | ConfigPlanChecks: resource.ConfigPlanChecks{ 48 | PreApply: []plancheck.PlanCheck{ 49 | plancheck.ExpectEmptyPlan(), 50 | }, 51 | }, 52 | }, 53 | { 54 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone) + testAccFreeIPADNSRecord_resource(testRecordMisordered), 55 | ConfigPlanChecks: resource.ConfigPlanChecks{ 56 | PreApply: []plancheck.PlanCheck{ 57 | plancheck.ExpectEmptyPlan(), 58 | }, 59 | }, 60 | }, 61 | }, 62 | }) 63 | } 64 | 65 | func TestAccFreeIPADNSRecord_A_CaseSensitive(t *testing.T) { 66 | testZone := map[string]string{ 67 | "index": "0", 68 | "zone_name": "\"ipa.example.lan\"", 69 | } 70 | testRecord := map[string]string{ 71 | "index": "0", 72 | "zone_name": "resource.freeipa_dns_zone.dns-zone-0.id", 73 | "type": "\"A\"", 74 | "name": "\"Test-Record\"", 75 | "records": "[\"192.168.10.10\", \"192.168.10.11\"]", 76 | } 77 | 78 | resource.Test(t, resource.TestCase{ 79 | PreCheck: func() { testAccPreCheck(t) }, 80 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 81 | Steps: []resource.TestStep{ 82 | { 83 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone) + testAccFreeIPADNSRecord_resource(testRecord), 84 | Check: resource.ComposeAggregateTestCheckFunc( 85 | resource.TestCheckResourceAttr("freeipa_dns_record.dns-record-0", "name", "Test-Record"), 86 | ), 87 | }, 88 | { 89 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone) + testAccFreeIPADNSRecord_resource(testRecord), 90 | ConfigPlanChecks: resource.ConfigPlanChecks{ 91 | PreApply: []plancheck.PlanCheck{ 92 | plancheck.ExpectEmptyPlan(), 93 | }, 94 | }, 95 | }, 96 | }, 97 | }) 98 | } 99 | 100 | func TestAccFreeIPADNSRecord_CNAME(t *testing.T) { 101 | testZone := map[string]string{ 102 | "index": "0", 103 | "zone_name": "\"ipa.example.lan\"", 104 | } 105 | testRecord := map[string]string{ 106 | "index": "0", 107 | "zone_name": "resource.freeipa_dns_zone.dns-zone-0.id", 108 | "name": "\"test-cname\"", 109 | "type": "\"CNAME\"", 110 | "records": "[\"test-record.ipa.example.lan.\"]", 111 | } 112 | 113 | resource.Test(t, resource.TestCase{ 114 | PreCheck: func() { testAccPreCheck(t) }, 115 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 116 | Steps: []resource.TestStep{ 117 | { 118 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone) + testAccFreeIPADNSRecord_resource(testRecord), 119 | Check: resource.ComposeAggregateTestCheckFunc( 120 | resource.TestCheckResourceAttr("freeipa_dns_record.dns-record-0", "name", "test-cname"), 121 | ), 122 | }, 123 | { 124 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone) + testAccFreeIPADNSRecord_resource(testRecord), 125 | ConfigPlanChecks: resource.ConfigPlanChecks{ 126 | PreApply: []plancheck.PlanCheck{ 127 | plancheck.ExpectEmptyPlan(), 128 | }, 129 | }, 130 | }, 131 | }, 132 | }) 133 | } 134 | 135 | func TestAccFreeIPADNSRecord_CNAME_CaseSensitive(t *testing.T) { 136 | testZone := map[string]string{ 137 | "index": "0", 138 | "zone_name": "\"ipa.example.lan\"", 139 | } 140 | testRecord := map[string]string{ 141 | "index": "0", 142 | "zone_name": "resource.freeipa_dns_zone.dns-zone-0.id", 143 | "name": "\"Test-CNAME\"", 144 | "type": "\"CNAME\"", 145 | "records": "[\"test-record.ipa.example.lan.\"]", 146 | } 147 | 148 | resource.Test(t, resource.TestCase{ 149 | PreCheck: func() { testAccPreCheck(t) }, 150 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 151 | Steps: []resource.TestStep{ 152 | { 153 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone) + testAccFreeIPADNSRecord_resource(testRecord), 154 | Check: resource.ComposeAggregateTestCheckFunc( 155 | resource.TestCheckResourceAttr("freeipa_dns_record.dns-record-0", "name", "Test-CNAME"), 156 | ), 157 | }, 158 | { 159 | Config: testAccFreeIPAProvider() + testAccFreeIPADNSZone_resource(testZone) + testAccFreeIPADNSRecord_resource(testRecord), 160 | ConfigPlanChecks: resource.ConfigPlanChecks{ 161 | PreApply: []plancheck.PlanCheck{ 162 | plancheck.ExpectEmptyPlan(), 163 | }, 164 | }, 165 | }, 166 | }, 167 | }) 168 | } 169 | -------------------------------------------------------------------------------- /docs/resources/user.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_user Resource - freeipa" 3 | description: |- 4 | FreeIPA User resource. The user lifecycle is managed with the attribute state: 5 | 6 | active 7 | disabled 8 | staged 9 | preserved 10 | (defaults to active) 11 | An active user can be preserved. 12 | A user can be staged at the user's creation or from a preservedstate. 13 | A staged user can be preserved. 14 | A preserved or staged user can activated. 15 | --- 16 | 17 | # freeipa_user (Resource) 18 | 19 | FreeIPA User resource. The user lifecycle is managed with the attribute state: 20 | 21 | - active 22 | 23 | - disabled 24 | 25 | - staged 26 | 27 | - preserved 28 | 29 | (defaults to active) 30 | 31 | An `active` user can be preserved. 32 | 33 | A user can be `staged` at the user's creation or from a `preserved`state. 34 | 35 | A `staged` user can be preserved. 36 | 37 | A `preserved` or `staged` user can activated. 38 | 39 | 40 | ## Example Usage 41 | 42 | ```terraform 43 | resource "freeipa_user" "user-1" { 44 | first_name = "Roman" 45 | last_name = "Roman" 46 | name = "roman" 47 | telephone_numbers = ["+380982555429", "2-10-11"] 48 | email_address = ["roman@example.com"] 49 | } 50 | 51 | resource "freeipa_user" "user-2" { 52 | first_name = "Roman" 53 | last_name = "Roman" 54 | name = "roman" 55 | state = "staged" 56 | telephone_numbers = ["+380982555429", "2-10-11"] 57 | email_address = ["roman@example.com"] 58 | } 59 | ``` 60 | 61 | 62 | 63 | ## Import Usage 64 | 65 | ```terraform 66 | # The import id of an active must be exactly equal to `username` of the user to import. 67 | 68 | # The associated resource in terraform must include the attributes: 69 | # - `name` 70 | # - `first_name` 71 | # - `last_name` 72 | 73 | import { 74 | to = freeipa_user.testuser 75 | id = "testuser" 76 | } 77 | 78 | resource "freeipa_user" "testuser" { 79 | name = "testuser" 80 | first_name = "Test" 81 | last_name = "User" 82 | } 83 | 84 | # The import id of n staged must be exactly equal to `username;staged` of the user to import. 85 | 86 | # The associated resource in terraform must include the attributes: 87 | # - `name` 88 | # - `first_name` 89 | # - `last_name` 90 | # - `state` 91 | 92 | import { 93 | to = freeipa_user.testuser 94 | id = "testuser;staged" 95 | } 96 | 97 | resource "freeipa_user" "testuser" { 98 | name = "testuser" 99 | first_name = "Test" 100 | last_name = "User" 101 | state = "staged 102 | } 103 | 104 | # The import id of a preserved must be exactly equal to `username;preserved` of the user to import. 105 | 106 | # The associated resource in terraform must include the attributes: 107 | # - `name` 108 | # - `first_name` 109 | # - `last_name` 110 | # - `state` 111 | 112 | import { 113 | to = freeipa_user.testuser 114 | id = "testuser;preserved" 115 | } 116 | 117 | resource "freeipa_user" "testuser" { 118 | name = "testuser" 119 | first_name = "Test" 120 | last_name = "User" 121 | state = "preserved" 122 | } 123 | ``` 124 | 125 | 126 | 127 | ## Schema 128 | 129 | ### Required 130 | 131 | - `first_name` (String) First name 132 | - `last_name` (String) Last name 133 | - `name` (String) UID or Login 134 | 135 | - The name must not exceed 32 characters. 136 | - The name must contain only lowercase letters (a-z), digits (0-9), and the characters (. - _). 137 | - The name must not start with a special character. 138 | - A user and a group cannot have the same name. 139 | 140 | ### Optional 141 | 142 | - `account_disabled` (Boolean, Deprecated) Account disabled. 143 | - `addattr` (List of String) Add an attribute/value pair. Format is attr=value. The attribute must be part of the LDAP schema. 144 | - `auth_type` (Set of String) User authentication type. Possible values of the elements are (password, radius, otp, pkinit, hardened, idp, passkey) 145 | - `car_license` (List of String) Car Licenses 146 | - `city` (String) City 147 | - `display_name` (String) Display name 148 | - `email_address` (List of String) Email address 149 | - `employee_number` (String) Employee Number 150 | - `employee_type` (String) Employee Type 151 | - `external_idp_config` (String) External IdP configuration 152 | - `external_idp_username` (String) External IdP user identifier 153 | - `full_name` (String) Full name 154 | - `gecos` (String) GECOS 155 | - `gid_number` (Number) Group ID Number 156 | - `home_directory` (String) Home Directory 157 | - `initials` (String) Initials 158 | - `job_title` (String) Job Title 159 | - `krb_password_expiration` (String) User password expiration [RFC3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.8) format (see [RFC3339 time string](https://tools.ietf.org/html/rfc3339#section-5.8) e.g., `YYYY-MM-DDTHH:MM:SSZ`) 160 | - `krb_principal_expiration` (String) Kerberos principal expiration [RFC3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.8) format (see [RFC3339 time string](https://tools.ietf.org/html/rfc3339#section-5.8) e.g., `YYYY-MM-DDTHH:MM:SSZ`) 161 | - `krb_principal_name` (List of String) Principal alias 162 | - `login_shell` (String) Login Shell 163 | - `manager` (String) Manager 164 | - `mobile_numbers` (List of String) Mobile Number 165 | - `organisation_unit` (String) Org. Unit 166 | - `postal_code` (String) Postal code 167 | - `preferred_language` (String) Preferred Language 168 | - `province` (String) Province/State/Country 169 | - `radius_proxy_config` (String) RADIUS proxy configuration 170 | - `radius_proxy_username` (String) RADIUS proxy username 171 | - `random_password` (Boolean) Generate a random user password 172 | - `setattr` (List of String) Set an attribute to a name/value pair. Format is attr=value. 173 | - `ssh_public_key` (List of String) List of SSH public keys 174 | - `state` (String) The current state of the user, can be `active`, `disabled`, `staged`, or `preserved` 175 | - `street_address` (String) Street address 176 | - `telephone_numbers` (List of String) Telephone Number 177 | - `uid_number` (Number) User ID Number (system will assign one if not provided) 178 | - `user_certificates` (Set of String) List of Base-64 encoded user certificates 179 | - `userclass` (List of String) User category (semantics placed on this attribute are for local interpretation) 180 | - `userpassword` (String, Sensitive) Prompt to set the user password 181 | 182 | ### Read-Only 183 | 184 | - `id` (String) ID of the resource 185 | -------------------------------------------------------------------------------- /freeipa/sudo_rule_test.go: -------------------------------------------------------------------------------- 1 | // Authors: 2 | // Antoine Gatineau 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-only 5 | 6 | package freeipa 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/hashicorp/terraform-plugin-testing/helper/resource" 12 | "github.com/hashicorp/terraform-plugin-testing/plancheck" 13 | ) 14 | 15 | func TestAccFreeIPASudoRule_simple(t *testing.T) { 16 | testSudoRule := map[string]string{ 17 | "index": "1", 18 | "name": "\"testacc-sudorule\"", 19 | "description": "\"A sudo rule for acceptance tests\"", 20 | } 21 | testSudoRuleModified := map[string]string{ 22 | "index": "1", 23 | "name": "\"testacc-sudorule\"", 24 | "description": "\"A new sudo rule for acceptance tests\"", 25 | "enabled": "false", 26 | "usercategory": "\"all\"", 27 | "hostcategory": "\"all\"", 28 | "commandcategory": "\"all\"", 29 | "runasusercategory": "\"all\"", 30 | "runasgroupcategory": "\"all\"", 31 | "order": "5", 32 | } 33 | testSudoDS := map[string]string{ 34 | "index": "1", 35 | "name": "freeipa_sudo_rule.sudorule-1.name", 36 | } 37 | 38 | resource.Test(t, resource.TestCase{ 39 | PreCheck: func() { testAccPreCheck(t) }, 40 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 41 | Steps: []resource.TestStep{ 42 | { 43 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoRule_resource(testSudoRule), 44 | Check: resource.ComposeAggregateTestCheckFunc( 45 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "name", "testacc-sudorule"), 46 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "description", "A sudo rule for acceptance tests"), 47 | ), 48 | }, 49 | { 50 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoRule_resource(testSudoRule) + testAccFreeIPASudoRule_datasource(testSudoDS), 51 | Check: resource.ComposeAggregateTestCheckFunc( 52 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "name", "testacc-sudorule"), 53 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "description", "A sudo rule for acceptance tests"), 54 | ), 55 | }, 56 | { 57 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoRule_resource(testSudoRule) + testAccFreeIPASudoRule_datasource(testSudoDS), 58 | ConfigPlanChecks: resource.ConfigPlanChecks{ 59 | PreApply: []plancheck.PlanCheck{ 60 | plancheck.ExpectEmptyPlan(), 61 | }, 62 | }, 63 | }, 64 | { 65 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoRule_resource(testSudoRuleModified), 66 | Check: resource.ComposeAggregateTestCheckFunc( 67 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "name", "testacc-sudorule"), 68 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "description", "A new sudo rule for acceptance tests"), 69 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "enabled", "false"), 70 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "usercategory", "all"), 71 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "hostcategory", "all"), 72 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "commandcategory", "all"), 73 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "runasusercategory", "all"), 74 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "runasgroupcategory", "all"), 75 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "order", "5"), 76 | ), 77 | }, 78 | { 79 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoRule_resource(testSudoRuleModified) + testAccFreeIPASudoRule_datasource(testSudoDS), 80 | Check: resource.ComposeAggregateTestCheckFunc( 81 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "name", "testacc-sudorule"), 82 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "description", "A new sudo rule for acceptance tests"), 83 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "enabled", "false"), 84 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "usercategory", "all"), 85 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "hostcategory", "all"), 86 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "commandcategory", "all"), 87 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "runasusercategory", "all"), 88 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "runasgroupcategory", "all"), 89 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "order", "5"), 90 | ), 91 | }, 92 | { 93 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoRule_resource(testSudoRuleModified) + testAccFreeIPASudoRule_datasource(testSudoDS), 94 | ConfigPlanChecks: resource.ConfigPlanChecks{ 95 | PreApply: []plancheck.PlanCheck{ 96 | plancheck.ExpectEmptyPlan(), 97 | }, 98 | }, 99 | }, 100 | }, 101 | }) 102 | } 103 | 104 | func TestAccFreeIPASudoRule_simple_CaseInsensitive(t *testing.T) { 105 | testSudoRule := map[string]string{ 106 | "index": "1", 107 | "name": "\"TestACC SudoRule\"", 108 | "description": "\"A sudo rule for acceptance tests\"", 109 | } 110 | testSudoDS := map[string]string{ 111 | "index": "1", 112 | "name": "freeipa_sudo_rule.sudorule-1.name", 113 | } 114 | 115 | resource.Test(t, resource.TestCase{ 116 | PreCheck: func() { testAccPreCheck(t) }, 117 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 118 | Steps: []resource.TestStep{ 119 | { 120 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoRule_resource(testSudoRule), 121 | Check: resource.ComposeAggregateTestCheckFunc( 122 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "id", "TestACC SudoRule"), 123 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "name", "TestACC SudoRule"), 124 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "description", "A sudo rule for acceptance tests"), 125 | ), 126 | }, 127 | { 128 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoRule_resource(testSudoRule) + testAccFreeIPASudoRule_datasource(testSudoDS), 129 | Check: resource.ComposeAggregateTestCheckFunc( 130 | resource.TestCheckResourceAttr("freeipa_sudo_rule.sudorule-1", "id", "TestACC SudoRule"), 131 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "name", "TestACC SudoRule"), 132 | resource.TestCheckResourceAttr("data.freeipa_sudo_rule.sudorule-1", "description", "A sudo rule for acceptance tests"), 133 | ), 134 | }, 135 | { 136 | Config: testAccFreeIPAProvider() + testAccFreeIPASudoRule_resource(testSudoRule) + testAccFreeIPASudoRule_datasource(testSudoDS), 137 | ConfigPlanChecks: resource.ConfigPlanChecks{ 138 | PreApply: []plancheck.PlanCheck{ 139 | plancheck.ExpectEmptyPlan(), 140 | }, 141 | }, 142 | }, 143 | }, 144 | }) 145 | } 146 | -------------------------------------------------------------------------------- /freeipa/hbac_policy_service_membership_test.go: -------------------------------------------------------------------------------- 1 | // Authors: 2 | // Antoine Gatineau 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-only 5 | 6 | package freeipa 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/hashicorp/terraform-plugin-testing/helper/resource" 12 | "github.com/hashicorp/terraform-plugin-testing/plancheck" 13 | ) 14 | 15 | func TestAccFreeIPAHbacPolicyServiceMembership_simple(t *testing.T) { 16 | testHbacPolicy := map[string]string{ 17 | "index": "1", 18 | "name": "\"testacc-hbac-policy\"", 19 | "description": "\"A hbac policy for acceptance tests\"", 20 | } 21 | testHbacServiceMembership := map[string]string{ 22 | "index": "1", 23 | "name": "freeipa_hbac_policy.hbacpolicy-1.name", 24 | "service": "\"sshd\"", 25 | } 26 | testHbacServiceGroupMembership := map[string]string{ 27 | "index": "2", 28 | "name": "freeipa_hbac_policy.hbacpolicy-1.name", 29 | "servicegroup": "\"Sudo\"", 30 | } 31 | testHbacDS := map[string]string{ 32 | "index": "1", 33 | "name": "freeipa_hbac_policy.hbacpolicy-1.name", 34 | } 35 | 36 | resource.Test(t, resource.TestCase{ 37 | PreCheck: func() { testAccPreCheck(t) }, 38 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 39 | Steps: []resource.TestStep{ 40 | { 41 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicy) + testAccFreeIPAHbacPolicyServiceMembership_resource(testHbacServiceMembership) + testAccFreeIPAHbacPolicyServiceMembership_resource(testHbacServiceGroupMembership), 42 | Check: resource.ComposeAggregateTestCheckFunc( 43 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "name", "testacc-hbac-policy"), 44 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "description", "A hbac policy for acceptance tests"), 45 | resource.TestCheckResourceAttr("freeipa_hbac_policy_service_membership.hbac-service-membership-1", "name", "testacc-hbac-policy"), 46 | resource.TestCheckResourceAttr("freeipa_hbac_policy_service_membership.hbac-service-membership-1", "service", "sshd"), 47 | resource.TestCheckResourceAttr("freeipa_hbac_policy_service_membership.hbac-service-membership-2", "name", "testacc-hbac-policy"), 48 | resource.TestCheckResourceAttr("freeipa_hbac_policy_service_membership.hbac-service-membership-2", "servicegroup", "Sudo"), 49 | ), 50 | }, 51 | { 52 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicy) + testAccFreeIPAHbacPolicyServiceMembership_resource(testHbacServiceMembership) + testAccFreeIPAHbacPolicyServiceMembership_resource(testHbacServiceGroupMembership) + testAccFreeIPAHbacPolicy_datasource(testHbacDS), 53 | Check: resource.ComposeAggregateTestCheckFunc( 54 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "name", "testacc-hbac-policy"), 55 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "description", "A hbac policy for acceptance tests"), 56 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "member_service.#", "1"), 57 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "member_service.0", "sshd"), 58 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "member_servicegroup.#", "1"), 59 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "member_servicegroup.0", "Sudo"), 60 | ), 61 | }, 62 | { 63 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicy) + testAccFreeIPAHbacPolicyServiceMembership_resource(testHbacServiceMembership) + testAccFreeIPAHbacPolicyServiceMembership_resource(testHbacServiceGroupMembership) + testAccFreeIPAHbacPolicy_datasource(testHbacDS), 64 | ConfigPlanChecks: resource.ConfigPlanChecks{ 65 | PreApply: []plancheck.PlanCheck{ 66 | plancheck.ExpectEmptyPlan(), 67 | }, 68 | }, 69 | }, 70 | }, 71 | }) 72 | } 73 | 74 | func TestAccFreeIPAHbacPolicyServiceMembership_mutiple(t *testing.T) { 75 | testHbacPolicy := map[string]string{ 76 | "index": "1", 77 | "name": "\"testacc-hbac-policy\"", 78 | "description": "\"A hbac policy for acceptance tests\"", 79 | } 80 | testHbacServiceMembership := map[string]string{ 81 | "index": "1", 82 | "name": "freeipa_hbac_policy.hbacpolicy-1.name", 83 | "services": "[\"sshd\"]", 84 | "identifier": "\"service-1\"", 85 | } 86 | testHbacServiceGroupMembership := map[string]string{ 87 | "index": "2", 88 | "name": "freeipa_hbac_policy.hbacpolicy-1.name", 89 | "servicegroups": "[\"Sudo\"]", 90 | "identifier": "\"servicegroup-2\"", 91 | } 92 | testHbacDS := map[string]string{ 93 | "index": "1", 94 | "name": "freeipa_hbac_policy.hbacpolicy-1.name", 95 | } 96 | resource.Test(t, resource.TestCase{ 97 | PreCheck: func() { testAccPreCheck(t) }, 98 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 99 | Steps: []resource.TestStep{ 100 | { 101 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicy) + testAccFreeIPAHbacPolicyServiceMembership_resource(testHbacServiceMembership) + testAccFreeIPAHbacPolicyServiceMembership_resource(testHbacServiceGroupMembership), 102 | Check: resource.ComposeAggregateTestCheckFunc( 103 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "name", "testacc-hbac-policy"), 104 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "description", "A hbac policy for acceptance tests"), 105 | resource.TestCheckResourceAttr("freeipa_hbac_policy_service_membership.hbac-service-membership-1", "name", "testacc-hbac-policy"), 106 | resource.TestCheckResourceAttr("freeipa_hbac_policy_service_membership.hbac-service-membership-1", "services.#", "1"), 107 | resource.TestCheckResourceAttr("freeipa_hbac_policy_service_membership.hbac-service-membership-1", "services.0", "sshd"), 108 | resource.TestCheckResourceAttr("freeipa_hbac_policy_service_membership.hbac-service-membership-2", "name", "testacc-hbac-policy"), 109 | resource.TestCheckResourceAttr("freeipa_hbac_policy_service_membership.hbac-service-membership-2", "servicegroups.#", "1"), 110 | resource.TestCheckResourceAttr("freeipa_hbac_policy_service_membership.hbac-service-membership-2", "servicegroups.0", "Sudo"), 111 | ), 112 | }, 113 | { 114 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicy) + testAccFreeIPAHbacPolicyServiceMembership_resource(testHbacServiceMembership) + testAccFreeIPAHbacPolicyServiceMembership_resource(testHbacServiceGroupMembership) + testAccFreeIPAHbacPolicy_datasource(testHbacDS), 115 | Check: resource.ComposeAggregateTestCheckFunc( 116 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "name", "testacc-hbac-policy"), 117 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "description", "A hbac policy for acceptance tests"), 118 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "member_service.#", "1"), 119 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "member_service.0", "sshd"), 120 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "member_servicegroup.#", "1"), 121 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "member_servicegroup.0", "Sudo"), 122 | ), 123 | }, 124 | { 125 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicy) + testAccFreeIPAHbacPolicyServiceMembership_resource(testHbacServiceMembership) + testAccFreeIPAHbacPolicyServiceMembership_resource(testHbacServiceGroupMembership) + testAccFreeIPAHbacPolicy_datasource(testHbacDS), 126 | ConfigPlanChecks: resource.ConfigPlanChecks{ 127 | PreApply: []plancheck.PlanCheck{ 128 | plancheck.ExpectEmptyPlan(), 129 | }, 130 | }, 131 | }, 132 | }, 133 | }) 134 | } 135 | -------------------------------------------------------------------------------- /freeipa/hbac_policy_data_source.go: -------------------------------------------------------------------------------- 1 | // This file was originally inspired by the module structure and design patterns 2 | // used in HashiCorp projects, but all code in this file was written from scratch. 3 | // 4 | // Previously licensed under the MPL-2.0. 5 | // This file is now relicensed under the GNU General Public License v3.0 only, 6 | // as permitted by Section 1.10 of the MPL. 7 | // 8 | // Authors: 9 | // Antoine Gatineau 10 | // 11 | // SPDX-License-Identifier: GPL-3.0-only 12 | 13 | package freeipa 14 | 15 | import ( 16 | "context" 17 | "fmt" 18 | 19 | "github.com/hashicorp/terraform-plugin-framework/datasource" 20 | "github.com/hashicorp/terraform-plugin-framework/datasource/schema" 21 | "github.com/hashicorp/terraform-plugin-framework/types" 22 | "github.com/hashicorp/terraform-plugin-log/tflog" 23 | ipa "github.com/infra-monkey/go-freeipa/freeipa" 24 | ) 25 | 26 | // Ensure provider defined types fully satisfy framework interfaces. 27 | var _ datasource.DataSource = &HbacPolicyDataSource{} 28 | var _ datasource.DataSourceWithConfigure = &HbacPolicyDataSource{} 29 | 30 | func NewHbacPolicyDataSource() datasource.DataSource { 31 | return &HbacPolicyDataSource{} 32 | } 33 | 34 | // HbacPolicyDataSource defines the resource implementation. 35 | type HbacPolicyDataSource struct { 36 | client *ipa.Client 37 | } 38 | 39 | // UserResourceModel describes the resource data model. 40 | type HbacPolicyDataSourceModel struct { 41 | Id types.String `tfsdk:"id"` 42 | Name types.String `tfsdk:"name"` 43 | Description types.String `tfsdk:"description"` 44 | Enabled types.Bool `tfsdk:"enabled"` 45 | UserCategory types.String `tfsdk:"usercategory"` 46 | HostCategory types.String `tfsdk:"hostcategory"` 47 | ServiceCategory types.String `tfsdk:"servicecategory"` 48 | MemberUser types.List `tfsdk:"member_user"` 49 | MemberGroup types.List `tfsdk:"member_group"` 50 | MemberHost types.List `tfsdk:"member_host"` 51 | MemberHostGroup types.List `tfsdk:"member_hostgroup"` 52 | MemberService types.List `tfsdk:"member_service"` 53 | MemberServiceGroup types.List `tfsdk:"member_servicegroup"` 54 | } 55 | 56 | func (r *HbacPolicyDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { 57 | resp.TypeName = req.ProviderTypeName + "_hbac_policy" 58 | } 59 | 60 | func (r *HbacPolicyDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { 61 | resp.Schema = schema.Schema{ 62 | // This description is used by the documentation generator and the language server. 63 | MarkdownDescription: "FreeIPA User hbac policy data source", 64 | 65 | Attributes: map[string]schema.Attribute{ 66 | "id": schema.StringAttribute{ 67 | MarkdownDescription: "ID of the resource in the terraform state", 68 | Computed: true, 69 | }, 70 | "name": schema.StringAttribute{ 71 | MarkdownDescription: "Name of the hbac policy", 72 | Required: true, 73 | }, 74 | "description": schema.StringAttribute{ 75 | MarkdownDescription: "Description of the hbac policy", 76 | Computed: true, 77 | }, 78 | "enabled": schema.BoolAttribute{ 79 | MarkdownDescription: "Enable this hbac policy", 80 | Computed: true, 81 | }, 82 | "usercategory": schema.StringAttribute{ 83 | MarkdownDescription: "User category the hbac policy is applied to (allowed value: all)", 84 | Computed: true, 85 | }, 86 | "hostcategory": schema.StringAttribute{ 87 | MarkdownDescription: "Host category the hbac policy is applied to (allowed value: all)", 88 | Computed: true, 89 | }, 90 | "servicecategory": schema.StringAttribute{ 91 | MarkdownDescription: "Command category the hbac policy is applied to (allowed value: all)", 92 | Computed: true, 93 | }, 94 | "member_user": schema.ListAttribute{ 95 | MarkdownDescription: "List of users member of this hbac policy.", 96 | Computed: true, 97 | ElementType: types.StringType, 98 | }, 99 | "member_group": schema.ListAttribute{ 100 | MarkdownDescription: "List of user groups member of this hbac policy.", 101 | Computed: true, 102 | ElementType: types.StringType, 103 | }, 104 | "member_host": schema.ListAttribute{ 105 | MarkdownDescription: "List of hosts member of this hbac policy.", 106 | Computed: true, 107 | ElementType: types.StringType, 108 | }, 109 | "member_hostgroup": schema.ListAttribute{ 110 | MarkdownDescription: "List of host groups member of this hbac policy.", 111 | Computed: true, 112 | ElementType: types.StringType, 113 | }, 114 | "member_service": schema.ListAttribute{ 115 | MarkdownDescription: "List of services member of this hbac policy.", 116 | Computed: true, 117 | ElementType: types.StringType, 118 | }, 119 | "member_servicegroup": schema.ListAttribute{ 120 | MarkdownDescription: "List of service groups member of this hbac policy.", 121 | Computed: true, 122 | ElementType: types.StringType, 123 | }, 124 | }, 125 | } 126 | } 127 | 128 | func (r *HbacPolicyDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { 129 | // Prevent panic if the provider has not been configured. 130 | if req.ProviderData == nil { 131 | return 132 | } 133 | 134 | client, ok := req.ProviderData.(*ipa.Client) 135 | 136 | if !ok { 137 | resp.Diagnostics.AddError( 138 | "Unexpected Resource Configure Type", 139 | fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), 140 | ) 141 | 142 | return 143 | } 144 | 145 | r.client = client 146 | } 147 | 148 | func (r *HbacPolicyDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { 149 | var data HbacPolicyDataSourceModel 150 | 151 | // Read Terraform configuration data into the model 152 | resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) 153 | 154 | if resp.Diagnostics.HasError() { 155 | return 156 | } 157 | 158 | all := true 159 | optArgs := ipa.HbacruleShowOptionalArgs{ 160 | All: &all, 161 | } 162 | 163 | args := ipa.HbacruleShowArgs{ 164 | Cn: data.Name.ValueString(), 165 | } 166 | 167 | res, err := r.client.HbacruleShow(&args, &optArgs) 168 | if err != nil { 169 | resp.Diagnostics.AddError("Client Error", err.Error()) 170 | return 171 | } 172 | 173 | if res.Result.Description != nil { 174 | data.Description = types.StringValue(*res.Result.Description) 175 | } 176 | if res.Result.Ipaenabledflag != nil { 177 | data.Enabled = types.BoolValue(*res.Result.Ipaenabledflag) 178 | } 179 | if res.Result.Usercategory != nil { 180 | data.UserCategory = types.StringValue(*res.Result.Usercategory) 181 | } 182 | if res.Result.Hostcategory != nil { 183 | data.HostCategory = types.StringValue(*res.Result.Hostcategory) 184 | } 185 | if res.Result.Servicecategory != nil { 186 | data.ServiceCategory = types.StringValue(*res.Result.Servicecategory) 187 | } 188 | if res.Result.MemberuserUser != nil { 189 | data.MemberUser, _ = types.ListValueFrom(ctx, types.StringType, res.Result.MemberuserUser) 190 | } 191 | if res.Result.MemberuserGroup != nil { 192 | data.MemberGroup, _ = types.ListValueFrom(ctx, types.StringType, res.Result.MemberuserGroup) 193 | } 194 | if res.Result.MemberhostHost != nil { 195 | data.MemberHost, _ = types.ListValueFrom(ctx, types.StringType, res.Result.MemberhostHost) 196 | } 197 | if res.Result.MemberhostHostgroup != nil { 198 | data.MemberHostGroup, _ = types.ListValueFrom(ctx, types.StringType, res.Result.MemberhostHostgroup) 199 | } 200 | if res.Result.MemberserviceHbacsvc != nil { 201 | data.MemberService, _ = types.ListValueFrom(ctx, types.StringType, res.Result.MemberserviceHbacsvc) 202 | } 203 | if res.Result.MemberserviceHbacsvcgroup != nil { 204 | data.MemberServiceGroup, _ = types.ListValueFrom(ctx, types.StringType, res.Result.MemberserviceHbacsvcgroup) 205 | } 206 | 207 | data.Id = data.Name 208 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Read freeipa hbac policy %s", res.Result.Cn)) 209 | 210 | // Save updated data into Terraform state 211 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 212 | if resp.Diagnostics.HasError() { 213 | return 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /freeipa/sudo_rule_option_resource.go: -------------------------------------------------------------------------------- 1 | // This file was originally inspired by the module structure and design patterns 2 | // used in HashiCorp projects, but all code in this file was written from scratch. 3 | // 4 | // Previously licensed under the MPL-2.0. 5 | // This file is now relicensed under the GNU General Public License v3.0 only, 6 | // as permitted by Section 1.10 of the MPL. 7 | // 8 | // Authors: 9 | // Antoine Gatineau 10 | // Mixton 11 | // 12 | // SPDX-License-Identifier: GPL-3.0-only 13 | 14 | package freeipa 15 | 16 | import ( 17 | "context" 18 | "fmt" 19 | "strings" 20 | 21 | "github.com/hashicorp/terraform-plugin-framework/resource" 22 | "github.com/hashicorp/terraform-plugin-framework/resource/schema" 23 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" 24 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" 25 | "github.com/hashicorp/terraform-plugin-framework/types" 26 | "github.com/hashicorp/terraform-plugin-log/tflog" 27 | ipa "github.com/infra-monkey/go-freeipa/freeipa" 28 | "golang.org/x/exp/slices" 29 | ) 30 | 31 | // Ensure provider defined types fully satisfy framework interfaces. 32 | var _ resource.Resource = &SudoRuleOptionResource{} 33 | 34 | func NewSudoRuleOptionResource() resource.Resource { 35 | return &SudoRuleOptionResource{} 36 | } 37 | 38 | // SudoRuleOptionResource defines the resource implementation. 39 | type SudoRuleOptionResource struct { 40 | client *ipa.Client 41 | } 42 | 43 | // SudoRuleOptionResourceModel describes the resource data model. 44 | type SudoRuleOptionResourceModel struct { 45 | Id types.String `tfsdk:"id"` 46 | Name types.String `tfsdk:"name"` 47 | Option types.String `tfsdk:"option"` 48 | } 49 | 50 | func (r *SudoRuleOptionResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { 51 | resp.TypeName = req.ProviderTypeName + "_sudo_rule_option" 52 | } 53 | 54 | func (r *SudoRuleOptionResource) ConfigValidators(ctx context.Context) []resource.ConfigValidator { 55 | return []resource.ConfigValidator{} 56 | } 57 | 58 | func (r *SudoRuleOptionResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { 59 | resp.Schema = schema.Schema{ 60 | // This description is used by the documentation generator and the language server. 61 | MarkdownDescription: "FreeIPA Sudo rule option resource", 62 | 63 | Attributes: map[string]schema.Attribute{ 64 | "id": schema.StringAttribute{ 65 | MarkdownDescription: "ID of the resource", 66 | Computed: true, 67 | PlanModifiers: []planmodifier.String{ 68 | stringplanmodifier.UseStateForUnknown(), 69 | }, 70 | }, 71 | "name": schema.StringAttribute{ 72 | MarkdownDescription: "Sudo rule name", 73 | Required: true, 74 | PlanModifiers: []planmodifier.String{ 75 | stringplanmodifier.RequiresReplace(), 76 | }, 77 | }, 78 | "option": schema.StringAttribute{ 79 | MarkdownDescription: "Sudo option to add to the sudo rule.", 80 | Required: true, 81 | PlanModifiers: []planmodifier.String{ 82 | stringplanmodifier.RequiresReplace(), 83 | }, 84 | }, 85 | }, 86 | } 87 | } 88 | 89 | func (r *SudoRuleOptionResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { 90 | // Prevent panic if the provider has not been configured. 91 | if req.ProviderData == nil { 92 | return 93 | } 94 | 95 | client, ok := req.ProviderData.(*ipa.Client) 96 | 97 | if !ok { 98 | resp.Diagnostics.AddError( 99 | "Unexpected Resource Configure Type", 100 | fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), 101 | ) 102 | 103 | return 104 | } 105 | 106 | r.client = client 107 | } 108 | 109 | func (r *SudoRuleOptionResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { 110 | var data SudoRuleOptionResourceModel 111 | var id string 112 | 113 | // Read Terraform plan data into the model 114 | resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) 115 | 116 | if resp.Diagnostics.HasError() { 117 | return 118 | } 119 | 120 | optArgs := ipa.SudoruleAddOptionOptionalArgs{} 121 | 122 | args := ipa.SudoruleAddOptionArgs{ 123 | Cn: data.Name.ValueString(), 124 | Ipasudoopt: []string{data.Option.ValueString()}, 125 | } 126 | _, err := r.client.SudoruleAddOption(&args, &optArgs) 127 | if err != nil { 128 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error creating freeipa sudo rule option: %s", err)) 129 | return 130 | } 131 | 132 | id = fmt.Sprintf("%s/sro/%s", encodeSlash(data.Name.ValueString()), data.Option.ValueString()) 133 | data.Id = types.StringValue(id) 134 | 135 | // Save data into Terraform state 136 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 137 | } 138 | 139 | func (r *SudoRuleOptionResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { 140 | var data SudoRuleOptionResourceModel 141 | 142 | // Read Terraform prior state data into the model 143 | resp.Diagnostics.Append(req.State.Get(ctx, &data)...) 144 | 145 | if resp.Diagnostics.HasError() { 146 | return 147 | } 148 | 149 | sudoruleId, typeId, optId, err := parseSudoRuleOptionID(data.Id.ValueString()) 150 | 151 | if err != nil { 152 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error parsing ID of freeipa_sudorule_host_membership: %s", err)) 153 | return 154 | } 155 | 156 | all := true 157 | optArgs := ipa.SudoruleShowOptionalArgs{ 158 | All: &all, 159 | } 160 | 161 | args := ipa.SudoruleShowArgs{ 162 | Cn: sudoruleId, 163 | } 164 | 165 | res, err := r.client.SudoruleShow(&args, &optArgs) 166 | if err != nil { 167 | if strings.Contains(err.Error(), "NotFound") { 168 | tflog.Debug(ctx, "[DEBUG] Sudo rule not found") 169 | resp.State.RemoveResource(ctx) 170 | return 171 | } else { 172 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error reading freeipa sudo rule: %s", err)) 173 | return 174 | } 175 | } 176 | 177 | switch typeId { 178 | case "sro": 179 | if res.Result.Ipasudoopt == nil || !slices.Contains(*res.Result.Ipasudoopt, optId) { 180 | tflog.Debug(ctx, "[DEBUG] Sudo rule option does not exist") 181 | resp.State.RemoveResource(ctx) 182 | return 183 | } 184 | } 185 | 186 | // Save updated data into Terraform state 187 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 188 | if resp.Diagnostics.HasError() { 189 | return 190 | } 191 | } 192 | 193 | func (r *SudoRuleOptionResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { 194 | var data, state SudoRuleOptionResourceModel 195 | 196 | // Read Terraform plan data into the model 197 | resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) 198 | resp.Diagnostics.Append(req.State.Get(ctx, &state)...) 199 | 200 | if resp.Diagnostics.HasError() { 201 | return 202 | } 203 | 204 | // Save updated data into Terraform state 205 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 206 | } 207 | 208 | func (r *SudoRuleOptionResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { 209 | var data SudoRuleOptionResourceModel 210 | 211 | // Read Terraform prior state data into the model 212 | resp.Diagnostics.Append(req.State.Get(ctx, &data)...) 213 | 214 | if resp.Diagnostics.HasError() { 215 | return 216 | } 217 | cmdgrpId, typeId, optId, err := parseSudoRuleOptionID(data.Id.ValueString()) 218 | 219 | if err != nil { 220 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error parsing ID of freeipa_sudo_rule_option: %s", err)) 221 | return 222 | } 223 | 224 | optArgs := ipa.SudoruleRemoveOptionOptionalArgs{} 225 | 226 | args := ipa.SudoruleRemoveOptionArgs{ 227 | Cn: cmdgrpId, 228 | } 229 | 230 | switch typeId { 231 | case "sro": 232 | args.Ipasudoopt = []string{optId} 233 | } 234 | 235 | _, err = r.client.SudoruleRemoveOption(&args, &optArgs) 236 | if err != nil { 237 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error delete freeipa sudo host membership: %s", err)) 238 | return 239 | } 240 | } 241 | 242 | func parseSudoRuleOptionID(id string) (string, string, string, error) { 243 | idParts := strings.SplitN(id, "/", 3) 244 | if len(idParts) < 3 { 245 | return "", "", "", fmt.Errorf("unable to determine sudo rule option ID %s", id) 246 | } 247 | 248 | name := decodeSlash(idParts[0]) 249 | _type := idParts[1] 250 | opt := idParts[2] 251 | 252 | return name, _type, opt, nil 253 | } 254 | -------------------------------------------------------------------------------- /freeipa/hbac_policy_test.go: -------------------------------------------------------------------------------- 1 | // Authors: 2 | // Antoine Gatineau 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-only 5 | 6 | package freeipa 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/hashicorp/terraform-plugin-testing/helper/resource" 12 | "github.com/hashicorp/terraform-plugin-testing/plancheck" 13 | ) 14 | 15 | func TestAccFreeIPAHbacPolicy_simple(t *testing.T) { 16 | testHbacPolicy := map[string]string{ 17 | "index": "1", 18 | "name": "\"testacc-hbac-policy\"", 19 | "description": "\"A hbac policy for acceptance tests\"", 20 | } 21 | testHbacPolicyModified := map[string]string{ 22 | "index": "1", 23 | "name": "\"testacc-hbac-policy\"", 24 | "description": "\"A new hbac policy for acceptance tests\"", 25 | "enabled": "false", 26 | "usercategory": "\"all\"", 27 | "hostcategory": "\"all\"", 28 | "servicecategory": "\"all\"", 29 | } 30 | testHbacPolicyDS := map[string]string{ 31 | "index": "1", 32 | "name": "freeipa_hbac_policy.hbacpolicy-1.name", 33 | } 34 | 35 | resource.Test(t, resource.TestCase{ 36 | PreCheck: func() { testAccPreCheck(t) }, 37 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 38 | Steps: []resource.TestStep{ 39 | { 40 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicy), 41 | Check: resource.ComposeAggregateTestCheckFunc( 42 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "name", "testacc-hbac-policy"), 43 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "description", "A hbac policy for acceptance tests"), 44 | ), 45 | }, 46 | { 47 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicy) + testAccFreeIPAHbacPolicy_datasource(testHbacPolicyDS), 48 | Check: resource.ComposeAggregateTestCheckFunc( 49 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "name", "testacc-hbac-policy"), 50 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "description", "A hbac policy for acceptance tests"), 51 | ), 52 | }, 53 | { 54 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicy) + testAccFreeIPAHbacPolicy_datasource(testHbacPolicyDS), 55 | ConfigPlanChecks: resource.ConfigPlanChecks{ 56 | PreApply: []plancheck.PlanCheck{ 57 | plancheck.ExpectEmptyPlan(), 58 | }, 59 | }, 60 | }, 61 | { 62 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicyModified), 63 | Check: resource.ComposeAggregateTestCheckFunc( 64 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "name", "testacc-hbac-policy"), 65 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "description", "A new hbac policy for acceptance tests"), 66 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "enabled", "false"), 67 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "usercategory", "all"), 68 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "hostcategory", "all"), 69 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "servicecategory", "all"), 70 | ), 71 | }, 72 | { 73 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicyModified) + testAccFreeIPAHbacPolicy_datasource(testHbacPolicyDS), 74 | Check: resource.ComposeAggregateTestCheckFunc( 75 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "name", "testacc-hbac-policy"), 76 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "description", "A new hbac policy for acceptance tests"), 77 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "enabled", "false"), 78 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "usercategory", "all"), 79 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "hostcategory", "all"), 80 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "servicecategory", "all"), 81 | ), 82 | }, 83 | { 84 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicyModified) + testAccFreeIPAHbacPolicy_datasource(testHbacPolicyDS), 85 | ConfigPlanChecks: resource.ConfigPlanChecks{ 86 | PreApply: []plancheck.PlanCheck{ 87 | plancheck.ExpectEmptyPlan(), 88 | }, 89 | }, 90 | }, 91 | }, 92 | }) 93 | } 94 | 95 | func TestAccFreeIPAHbacPolicy_simple_CaseSensitive(t *testing.T) { 96 | testHbacPolicy := map[string]string{ 97 | "index": "1", 98 | "name": "\"Testacc HBAC Policy\"", 99 | "description": "\"A hbac policy for acceptance tests\"", 100 | } 101 | testHbacPolicyModified := map[string]string{ 102 | "index": "1", 103 | "name": "\"Testacc HBAC Policy\"", 104 | "description": "\"A new hbac policy for acceptance tests\"", 105 | "enabled": "false", 106 | "usercategory": "\"all\"", 107 | "hostcategory": "\"all\"", 108 | "servicecategory": "\"all\"", 109 | } 110 | testHbacPolicyDS := map[string]string{ 111 | "index": "1", 112 | "name": "freeipa_hbac_policy.hbacpolicy-1.name", 113 | } 114 | 115 | resource.Test(t, resource.TestCase{ 116 | PreCheck: func() { testAccPreCheck(t) }, 117 | ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, 118 | Steps: []resource.TestStep{ 119 | { 120 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicy), 121 | Check: resource.ComposeAggregateTestCheckFunc( 122 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "name", "Testacc HBAC Policy"), 123 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "description", "A hbac policy for acceptance tests"), 124 | ), 125 | }, 126 | { 127 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicy) + testAccFreeIPAHbacPolicy_datasource(testHbacPolicyDS), 128 | Check: resource.ComposeAggregateTestCheckFunc( 129 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "name", "Testacc HBAC Policy"), 130 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "description", "A hbac policy for acceptance tests"), 131 | ), 132 | }, 133 | { 134 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicy) + testAccFreeIPAHbacPolicy_datasource(testHbacPolicyDS), 135 | ConfigPlanChecks: resource.ConfigPlanChecks{ 136 | PreApply: []plancheck.PlanCheck{ 137 | plancheck.ExpectEmptyPlan(), 138 | }, 139 | }, 140 | }, 141 | { 142 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicyModified), 143 | Check: resource.ComposeAggregateTestCheckFunc( 144 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "name", "Testacc HBAC Policy"), 145 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "description", "A new hbac policy for acceptance tests"), 146 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "enabled", "false"), 147 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "usercategory", "all"), 148 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "hostcategory", "all"), 149 | resource.TestCheckResourceAttr("freeipa_hbac_policy.hbacpolicy-1", "servicecategory", "all"), 150 | ), 151 | }, 152 | { 153 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicyModified) + testAccFreeIPAHbacPolicy_datasource(testHbacPolicyDS), 154 | Check: resource.ComposeAggregateTestCheckFunc( 155 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "name", "Testacc HBAC Policy"), 156 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "description", "A new hbac policy for acceptance tests"), 157 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "enabled", "false"), 158 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "usercategory", "all"), 159 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "hostcategory", "all"), 160 | resource.TestCheckResourceAttr("data.freeipa_hbac_policy.hbacpolicy-1", "servicecategory", "all"), 161 | ), 162 | }, 163 | { 164 | Config: testAccFreeIPAProvider() + testAccFreeIPAHbacPolicy_resource(testHbacPolicyModified) + testAccFreeIPAHbacPolicy_datasource(testHbacPolicyDS), 165 | ConfigPlanChecks: resource.ConfigPlanChecks{ 166 | PreApply: []plancheck.PlanCheck{ 167 | plancheck.ExpectEmptyPlan(), 168 | }, 169 | }, 170 | }, 171 | }, 172 | }) 173 | } 174 | --------------------------------------------------------------------------------