├── .github ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE │ ├── bug-report.md │ ├── feature-request.md │ └── question.md └── workflows │ ├── release.yml │ └── test-acc.yml ├── .gitignore ├── .goreleaser.yml ├── GNUmakefile ├── ISSUES.md ├── LICENSE ├── OUR_CONTRIBUTORS.md ├── README.md ├── _about └── CONTRIBUTING.md ├── docker_compose └── docker-compose.yml ├── docs ├── data-sources │ ├── dns_record.md │ ├── dns_zone.md │ ├── group.md │ ├── hbac_policy.md │ ├── host.md │ ├── hostgroup.md │ ├── sudo_cmdgroup.md │ ├── sudo_rule.md │ └── user.md ├── index.md └── resources │ ├── automemberadd.md │ ├── automemberadd_condition.md │ ├── dns_record.md │ ├── dns_zone.md │ ├── group.md │ ├── hbac_policy.md │ ├── hbac_policy_host_membership.md │ ├── hbac_policy_service_membership.md │ ├── hbac_policy_user_membership.md │ ├── host.md │ ├── host_hostgroup_membership.md │ ├── hostgroup.md │ ├── sudo_cmd.md │ ├── sudo_cmdgroup.md │ ├── sudo_cmdgroup_membership.md │ ├── sudo_rule.md │ ├── sudo_rule_allowcmd_membership.md │ ├── sudo_rule_denycmd_membership.md │ ├── sudo_rule_host_membership.md │ ├── sudo_rule_option.md │ ├── sudo_rule_runasgroup_membership.md │ ├── sudo_rule_runasuser_membership.md │ ├── sudo_rule_user_membership.md │ ├── user.md │ └── user_group_membership.md ├── examples ├── data-sources │ ├── freeipa_dns_record │ │ └── data-source.tf │ ├── freeipa_dns_zone │ │ └── data-source.tf │ ├── freeipa_group │ │ └── data-source.tf │ ├── freeipa_hbac_policy │ │ └── data-source.tf │ ├── freeipa_host │ │ └── data-source.tf │ ├── freeipa_hostgroup │ │ └── data-source.tf │ ├── freeipa_sudo_cmdgroup │ │ └── data-source.tf │ ├── freeipa_sudo_rule │ │ └── data-source.tf │ └── freeipa_user │ │ └── data-source.tf ├── provider │ └── provider.tf └── resources │ ├── freeipa_automemberadd │ └── resource.tf │ ├── freeipa_automemberadd_condition │ └── resource.tf │ ├── freeipa_dns_record │ └── resource.tf │ ├── freeipa_dns_zone │ └── resource.tf │ ├── freeipa_group │ └── resource.tf │ ├── freeipa_hbac_policy │ └── resource.tf │ ├── freeipa_hbac_policy_host_membership │ └── resource.tf │ ├── freeipa_hbac_policy_service_membership │ └── resource.tf │ ├── freeipa_hbac_policy_user_membership │ └── resource.tf │ ├── freeipa_host │ └── resource.tf │ ├── freeipa_host_hostgroup_membership │ └── resource.tf │ ├── freeipa_hostgroup │ └── resource.tf │ ├── freeipa_sudo_cmd │ └── resource.tf │ ├── freeipa_sudo_cmdgroup │ └── resource.tf │ ├── freeipa_sudo_cmdgroup_membership │ └── resource.tf │ ├── freeipa_sudo_rule │ └── resource.tf │ ├── freeipa_sudo_rule_allowcmd_membership │ └── resource.tf │ ├── freeipa_sudo_rule_denycmd_membership │ └── resource.tf │ ├── freeipa_sudo_rule_host_membership │ └── resource.tf │ ├── freeipa_sudo_rule_option │ └── resource.tf │ ├── freeipa_sudo_rule_runasgroup_membership │ └── resource.tf │ ├── freeipa_sudo_rule_runasuser_membership │ └── resource.tf │ ├── freeipa_sudo_rule_user_membership │ └── resource.tf │ ├── freeipa_user │ └── resource.tf │ └── freeipa_user_group_membership │ └── resource.tf ├── freeipa ├── automember_condition_resource.go ├── automember_resource.go ├── dns_record_data_source.go ├── dns_record_resource.go ├── dns_record_test.go ├── dns_zone_data_source.go ├── dns_zone_resource.go ├── dns_zone_test.go ├── group_data_source.go ├── group_resource.go ├── group_test.go ├── hbac_policy_data_source.go ├── hbac_policy_host_membership_resource.go ├── hbac_policy_host_membership_test.go ├── hbac_policy_resource.go ├── hbac_policy_service_membership_resource .go ├── hbac_policy_service_membership_test.go ├── hbac_policy_test.go ├── hbac_policy_user_membership_resource.go ├── hbac_policy_user_membership_test.go ├── helper_test.go ├── host_data_source.go ├── host_hostgroup_membership_resource.go ├── host_hostgroup_membership_test.go ├── host_resource.go ├── host_test.go ├── hostgroup_data_source.go ├── hostgroup_resource .go ├── hostgroup_test.go ├── provider.go ├── provider_test.go ├── sudo_cmd_resource.go ├── sudo_cmd_test.go ├── sudo_cmdgroup_data_source.go ├── sudo_cmdgroup_membership_resource.go ├── sudo_cmdgroup_membership_test.go ├── sudo_cmdgroup_resource.go ├── sudo_cmdgroup_test.go ├── sudo_rule_allowcmd_membership_resource.go ├── sudo_rule_allowcmd_membership_test.go ├── sudo_rule_data_source.go ├── sudo_rule_denycmd_membership_resource.go ├── sudo_rule_denycmd_membership_test.go ├── sudo_rule_host_membership_resource.go ├── sudo_rule_host_membership_test.go ├── sudo_rule_option_resource.go ├── sudo_rule_option_test.go ├── sudo_rule_resource.go ├── sudo_rule_runasgroup_membership_resource.go ├── sudo_rule_runasgroup_membership_test.go ├── sudo_rule_runasuser_membership_resource.go ├── sudo_rule_runasuser_membership_test.go ├── sudo_rule_test.go ├── sudo_rule_user_membership_resource.go ├── sudo_rule_user_membership_test.go ├── user_data_source.go ├── user_group_membership_resource.go ├── user_group_membership_test.go ├── user_resource.go ├── user_test.go └── utils.go ├── go.mod ├── go.sum ├── main.go ├── resource.template ├── templates ├── data-sources.md.tmpl ├── index.md.tmpl └── resources.md.tmpl ├── terraform-registry-manifest.json └── tools ├── go.mod ├── go.sum └── tools.go /.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 | -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | ``` -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | terraform-provider-freeipa-2 2 | terraform-provider-freeipa 3 | .env 4 | test/ 5 | test-migv4/ -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /ISSUES.md: -------------------------------------------------------------------------------- 1 | error returned when enabling/disabling a zone but it succeeds -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Terraform FreeIPA Provider 2 | ============================ 3 | Tested on FreeIPA version 4.9.1 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. -------------------------------------------------------------------------------- /_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 | -------------------------------------------------------------------------------- /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-37-4.10.1 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: -------------------------------------------------------------------------------- /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 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | data "freeipa_dns_record" "dns-record-0" { 15 | record_name = "test-record-A" 16 | zone_name = "test.example.lan." 17 | } 18 | 19 | data "freeipa_dns_record" "dns-zone-1" { 20 | record_name = "10" 21 | zone_name = "23.168.192.in-addr.arpa." 22 | } 23 | ``` 24 | 25 | 26 | 27 | ## Schema 28 | 29 | ### Required 30 | 31 | - `record_name` (String) Record name 32 | - `zone_name` (String) Zone name (FQDN) 33 | 34 | ### Read-Only 35 | 36 | - `id` (String) ID of the resource 37 | -------------------------------------------------------------------------------- /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 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | data "freeipa_dns_zone" "dns-zone-0" { 15 | zone_name = "test.example.lan." 16 | } 17 | 18 | data "freeipa_dns_zone" "dns-zone-1" { 19 | zone_name = "23.168.192.in-addr.arpa." 20 | } 21 | ``` 22 | 23 | 24 | 25 | ## Schema 26 | 27 | ### Required 28 | 29 | - `zone_name` (String) Zone name (FQDN) 30 | 31 | ### Read-Only 32 | 33 | - `admin_email_address` (String) Administrator e-mail address 34 | - `allow_inline_dnssec_signing` (Boolean) Allow inline DNSSEC signing of records in the zone 35 | - `allow_prt_sync` (Boolean) Allow synchronization of forward (A, AAAA) and reverse (PTR) records in the zone 36 | - `allow_query` (String) Semicolon separated list of IP addresses or networks which are allowed to issue queries 37 | - `allow_transfer` (String) Semicolon separated list of IP addresses or networks which are allowed to transfer the zone 38 | - `authoritative_nameserver` (String) Authoritative nameserver domain name 39 | - `bind_update_policy` (String) BIND update policy 40 | - `default_ttl` (Number) Time to live for records without explicit TTL definition 41 | - `disable_zone` (Boolean) Allow disabled the zone 42 | - `dynamic_updates` (Boolean) Allow dynamic updates 43 | - `id` (String) ID of the resource 44 | - `nsec3param_record` (String) NSEC3PARAM record for zone in format: hash_algorithm flags iterations salt 45 | - `skip_nameserver_check` (Boolean) Force DNS zone creation even if nameserver is not resolvable 46 | - `skip_overlap_check` (Boolean) Force DNS zone creation even if it will overlap with an existing zone 47 | - `soa_expire` (Number) SOA record expire time 48 | - `soa_minimum` (Number) How long should negative responses be cached 49 | - `soa_refresh` (Number) SOA record refresh time 50 | - `soa_retry` (Number) SOA record retry time 51 | - `soa_serial_number` (Number) SOA record serial number 52 | - `ttl` (Number) Time to live for records at zone apex 53 | - `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 54 | -------------------------------------------------------------------------------- /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 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | data "freeipa_group" "group-0" { 15 | name = "test-group" 16 | } 17 | ``` 18 | 19 | 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `name` (String) Group name 26 | 27 | - The name must not exceed 32 characters. 28 | - The name must contain only lowercase letters (a-z), digits (0-9), and the characters (. - _). 29 | - The name must not start with a special character. 30 | - A user and a group cannot have the same name. 31 | 32 | ### Read-Only 33 | 34 | - `description` (String) Group Description 35 | - `gid_number` (Number) GID (use this option to set it manually) 36 | - `id` (String) ID of the resource in the terraform state 37 | - `member_external` (List of String) List of external users (from trusted domain) that are member of this group. 38 | - `member_group` (List of String) List of groups that are member of this group. 39 | - `member_indirect_group` (List of String) List of groups that are is indirectly member of this group. 40 | - `member_indirect_user` (List of String) List of users that are is indirectly member of this group. 41 | - `member_user` (List of String) List of users that are member of this group. 42 | - `memberof_group` (List of String) List of groups this group is member of. 43 | - `memberof_hbacrule` (List of String) List of HBAC rules this group is member of. 44 | - `memberof_indirect_group` (List of String) List of groups this group is is indirectly member of. 45 | - `memberof_indirect_hbacrule` (List of String) List of HBAC rules this group is indirectly member of. 46 | - `memberof_indirect_sudorule` (List of String) List of SUDO rules this group is is indirectly member of. 47 | - `memberof_sudorule` (List of String) List of SUDO rules this group is member of. 48 | -------------------------------------------------------------------------------- /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 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | data "freeipa_hbac_policy" "myservers" { 15 | name = "myservers" 16 | } 17 | ``` 18 | 19 | 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `name` (String) Name of the hbac policy 26 | 27 | ### Read-Only 28 | 29 | - `description` (String) Description of the hbac policy 30 | - `enabled` (Boolean) Enable this hbac policy 31 | - `hostcategory` (String) Host category the hbac policy is applied to (allowed value: all) 32 | - `id` (String) ID of the resource in the terraform state 33 | - `member_group` (List of String) List of user groups member of this hbac policy. 34 | - `member_host` (List of String) List of hosts member of this hbac policy. 35 | - `member_hostgroup` (List of String) List of host groups member of this hbac policy. 36 | - `member_service` (List of String) List of services member of this hbac policy. 37 | - `member_servicegroup` (List of String) List of service groups member of this hbac policy. 38 | - `member_user` (List of String) List of users member of this hbac policy. 39 | - `servicecategory` (String) Command category the hbac policy is applied to (allowed value: all) 40 | - `usercategory` (String) User category the hbac policy is applied to (allowed value: all) 41 | -------------------------------------------------------------------------------- /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 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | data "freeipa_host" "host-0" { 15 | name = "testhost.example.lan" 16 | } 17 | ``` 18 | 19 | 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `name` (String) Host fully qualified name 26 | 27 | - May contain only letters, numbers, '-'. 28 | - DNS label may not start or end with '-' 29 | 30 | ### Optional 31 | 32 | - `trusted_for_delegation` (Boolean) Client credentials may be delegated to the service 33 | 34 | ### Read-Only 35 | 36 | - `assigned_idview` (String) Assigned ID View 37 | - `description` (String) A description of this host 38 | - `id` (String) ID of the resource in the terraform state 39 | - `ipasshpubkeys` (List of String) SSH public keys 40 | - `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. 41 | - `krb_preauth` (Boolean) Pre-authentication is required for the service 42 | - `locality` (String) Host locality (e.g. 'Baltimore, MD') 43 | - `location` (String) Host location (e.g. 'Lab 2') 44 | - `mac_addresses` (List of String) Hardware MAC address(es) on this host 45 | - `memberof_hbacrule` (List of String) List of HBAC rules this user is member of. 46 | - `memberof_hostgroup` (List of String) List of hostgroups this user is member of. 47 | - `memberof_indirect_hbacrule` (List of String) List of HBAC rules this user is indirectly member of. 48 | - `memberof_indirect_hostgroup` (List of String) List of hostgroups this user is is indirectly member of. 49 | - `memberof_indirect_sudorule` (List of String) List of SUDO rules this user is is indirectly member of. 50 | - `memberof_sudorule` (List of String) List of SUDO rules this user is member of. 51 | - `operating_system` (String) Host operating system and version (e.g. 'Fedora 40') 52 | - `platform` (String) Host hardware platform (e.g. 'Lenovo T61') 53 | - `trusted_to_auth_as_delegate` (Boolean) The service is allowed to authenticate on behalf of a client 54 | - `user_certificates` (List of String) Base-64 encoded host certificate 55 | - `userclass` (List of String) Host category (semantics placed on this attribute are for local interpretation) 56 | -------------------------------------------------------------------------------- /docs/data-sources/hostgroup.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_hostgroup Data Source - freeipa" 3 | description: |- 4 | FreeIPA User Group data source 5 | --- 6 | 7 | # freeipa_hostgroup (Data Source) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | data "freeipa_hostgroup" "hostgroup-0" { 15 | name = "testhostgroup" 16 | } 17 | ``` 18 | 19 | 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `name` (String) Hostgroup name 26 | 27 | ### Read-Only 28 | 29 | - `description` (String) Hostgroup Description 30 | - `id` (String) ID of the resource in the terraform state 31 | - `member_host` (List of String) List of hosts that are member of this hostgroup. 32 | - `member_hostgroup` (List of String) List of hostgroups that are member of this hostgroup. 33 | - `member_indirect_host` (List of String) List of hosts that are is indirectly member of this hostgroup. 34 | - `member_indirect_hostgroup` (List of String) List of hostgroups that are is indirectly member of this hostgroup. 35 | - `memberof_hbacrule` (List of String) List of HBAC rules this hostgroup is member of. 36 | - `memberof_hostgroup` (List of String) List of hostgroups this hostgroup is member of. 37 | - `memberof_indirect_hbacrule` (List of String) List of HBAC rules this hostgroup is indirectly member of. 38 | - `memberof_indirect_hostgroup` (List of String) List of hostgroups this hostgroup is is indirectly member of. 39 | - `memberof_indirect_sudorule` (List of String) List of SUDO rules this hostgroup is is indirectly member of. 40 | - `memberof_sudorule` (List of String) List of SUDO rules this hostgroup is member of. 41 | -------------------------------------------------------------------------------- /docs/data-sources/sudo_cmdgroup.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_sudo_cmdgroup Data Source - freeipa" 3 | description: |- 4 | FreeIPA User sudo command group data source 5 | --- 6 | 7 | # freeipa_sudo_cmdgroup (Data Source) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | data "freeipa_sudo_cmdgroup" "terminals" { 15 | name = "terminals" 16 | } 17 | ``` 18 | 19 | 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `name` (String) Name of the sudo command group 26 | 27 | ### Read-Only 28 | 29 | - `description` (String) Description of the sudo command group 30 | - `id` (String) ID of the resource in the terraform state 31 | - `member_sudocmd` (List of String) List of sudo commands that are member of the sudo command group 32 | -------------------------------------------------------------------------------- /docs/data-sources/sudo_rule.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_sudo_rule Data Source - freeipa" 3 | description: |- 4 | FreeIPA User sudo rule data source 5 | --- 6 | 7 | # freeipa_sudo_rule (Data Source) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | data "freeipa_sudo_rule" "operators" { 15 | name = "operators" 16 | } 17 | ``` 18 | 19 | 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `name` (String) Name of the sudo rule 26 | 27 | ### Read-Only 28 | 29 | - `commandcategory` (String) Command category the sudo rule is applied to (allowed value: all) 30 | - `description` (String) Description of the sudo rule 31 | - `enabled` (Boolean) Enable this sudo rule 32 | - `hostcategory` (String) Host category the sudo rule is applied to (allowed value: all) 33 | - `id` (String) ID of the resource in the terraform state 34 | - `member_allow_sudo_cmd` (List of String) List of allowed sudo commands member of this sudo rule. 35 | - `member_allow_sudo_cmdgroup` (List of String) List of allowed sudo command groups member of this sudo rule. 36 | - `member_deny_sudo_cmd` (List of String) List of denied sudo commands member of this sudo rule. 37 | - `member_deny_sudo_cmdgroup` (List of String) List of denied sudo command groups member of this sudo rule. 38 | - `member_group` (List of String) List of user groups member of this sudo rule. 39 | - `member_host` (List of String) List of hosts member of this sudo rule. 40 | - `member_hostgroup` (List of String) List of host groups member of this sudo rule. 41 | - `member_user` (List of String) List of users member of this sudo rule. 42 | - `option` (List of String) List of options defined for this sudo rule. 43 | - `order` (Number) Sudo rule order (must be unique) 44 | - `runasgroup` (List of String) List of groups authorised to be run as. 45 | - `runasgroupcategory` (String) Run as group category the sudo rule is applied to (allowed value: all) 46 | - `runasuser` (List of String) List of users authorised to be run as. 47 | - `runasusercategory` (String) Run as user category the sudo rule is applied to (allowed value: all) 48 | - `usercategory` (String) User category the sudo rule is applied to (allowed value: all) 49 | -------------------------------------------------------------------------------- /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 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | data "freeipa_user" "user-0" { 15 | name = "test-user" 16 | first_name = "John" 17 | last_name = "Doe" 18 | } 19 | ``` 20 | 21 | 22 | 23 | ## Schema 24 | 25 | ### Required 26 | 27 | - `name` (String) UID or Login 28 | 29 | - The name must not exceed 32 characters. 30 | - The name must contain only lowercase letters (a-z), digits (0-9), and the characters (. - _). 31 | - The name must not start with a special character. 32 | - A user and a group cannot have the same name. 33 | 34 | ### Read-Only 35 | 36 | - `account_disabled` (Boolean) Account disabled 37 | - `car_license` (List of String) Car Licenses 38 | - `city` (String) City 39 | - `display_name` (String) Display name 40 | - `email_address` (List of String) Email address 41 | - `employee_number` (String) Employee Number 42 | - `employee_type` (String) Employee Type 43 | - `first_name` (String) First name 44 | - `full_name` (String) Full name 45 | - `gecos` (String) GECOS 46 | - `gid_number` (Number) Group ID Number 47 | - `home_directory` (String) Home Directory 48 | - `id` (String) ID of the resource in the terraform state 49 | - `initials` (String) Initials 50 | - `job_title` (String) Job Title 51 | - `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`) 52 | - `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`) 53 | - `krb_principal_name` (List of String) Principal alias 54 | - `last_name` (String) Last name 55 | - `login_shell` (String) Login Shell 56 | - `manager` (String) Manager 57 | - `memberof_group` (List of String) List of groups this user is member of. 58 | - `memberof_hbacrule` (List of String) List of HBAC rules this user is member of. 59 | - `memberof_indirect_group` (List of String) List of groups this user is is indirectly member of. 60 | - `memberof_indirect_hbacrule` (List of String) List of HBAC rules this user is indirectly member of. 61 | - `memberof_indirect_sudorule` (List of String) List of SUDO rules this user is is indirectly member of. 62 | - `memberof_sudorule` (List of String) List of SUDO rules this user is member of. 63 | - `mobile_numbers` (List of String) Mobile Number 64 | - `organisation_unit` (String) Org. Unit 65 | - `postal_code` (String) Postal code 66 | - `preferred_language` (String) Preferred Language 67 | - `province` (String) Province/State/Country 68 | - `random_password` (Boolean) Generate a random user password 69 | - `ssh_public_key` (List of String) List of SSH public keys 70 | - `street_address` (String) Street address 71 | - `telephone_numbers` (List of String) Telephone Number 72 | - `uid_number` (Number) User ID Number (system will assign one if not provided) 73 | - `userclass` (List of String) User category (semantics placed on this attribute are for local interpretation) 74 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Provider: FREEIPA" 3 | description: |- 4 | 5 | --- 6 | 7 | # FREEIPA Provider 8 | 9 | ## Example Usage 10 | 11 | ```terraform 12 | terraform { 13 | required_providers { 14 | freeipa = { 15 | version = "0.1.1" 16 | source = "[Terraform registry provider path]" 17 | } 18 | } 19 | } 20 | 21 | provider "freeipa" { 22 | host = "ipa.example.test" 23 | username = "admin" 24 | password = "123456789" 25 | insecure = true 26 | } 27 | ``` 28 | 29 | 30 | ## Schema 31 | 32 | ### Optional 33 | 34 | - `ca_certificate` (String) Path to the server's SSL CA certificate 35 | - `host` (String) The FreeIPA host 36 | - `insecure` (Boolean) Whether to verify the server's SSL certificate 37 | - `password` (String, Sensitive) Password to use for connection 38 | - `username` (String) Username to use for connection 39 | -------------------------------------------------------------------------------- /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 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_hostgroup" "hostgroup" { 15 | name = "my-hostgroup" 16 | description = "my-hostgroup desc" 17 | } 18 | 19 | resource "freeipa_automemberadd" "automember" { 20 | name = freeipa_hostgroup.hostgroup.name 21 | type = "hostgroup" 22 | } 23 | ``` 24 | 25 | 26 | 27 | 28 | 29 | ## Schema 30 | 31 | ### Required 32 | 33 | - `name` (String) Automember rule name 34 | - `type` (String) Automember rule type 35 | 36 | ### Optional 37 | 38 | - `addattr` (List of String) Add an attribute/value pair. Format is attr=value. The attribute must be part of the schema. 39 | - `description` (String) Automember rule description 40 | - `setattr` (List of String) Set an attribute to a name/value pair. Format is attr=value. 41 | 42 | ### Read-Only 43 | 44 | - `id` (String) ID of the resource 45 | -------------------------------------------------------------------------------- /docs/resources/automemberadd_condition.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_automemberadd_condition Resource - freeipa" 3 | description: |- 4 | FreeIPA Automember conditionresource 5 | --- 6 | 7 | # freeipa_automemberadd_condition (Resource) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_hostgroup" "hostgroup" { 15 | name = "my-hostgroup" 16 | description = "my-hostgroup desc" 17 | } 18 | 19 | resource "freeipa_automemberadd" "automember" { 20 | name = freeipa_hostgroup.hostgroup.name 21 | type = "hostgroup" 22 | } 23 | 24 | resource "freeipa_automemberadd_condition" "automembercondition" { 25 | name = freeipa_automemberadd.automember.name 26 | type = "hostgroup" 27 | key = "fqdn" 28 | inclusiveregex = ["\\.my\\.first\\.net$", "\\.my\\.second\\.net$"] 29 | } 30 | ``` 31 | 32 | 33 | 34 | 35 | 36 | ## Schema 37 | 38 | ### Required 39 | 40 | - `key` (String) Automember rule condition key 41 | - `name` (String) Automember rule condition name 42 | - `type` (String) Automember rule condition type 43 | 44 | ### Optional 45 | 46 | - `description` (String) Automember rule condition description 47 | - `exclusiveregex` (List of String) Regex expression for values that should be excluded. 48 | - `inclusiveregex` (List of String) Regex expression for values that should be included. 49 | 50 | ### Read-Only 51 | 52 | - `id` (String) ID of the resource 53 | -------------------------------------------------------------------------------- /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 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_dns_zone" "dns_zone-2" { 15 | zone_name = "test.roman.com.ua." 16 | skip_overlap_check = true 17 | } 18 | 19 | resource "freeipa_dns_record" "record-8" { 20 | zone_name = resource.freeipa_dns_zone.dns_zone-2.id 21 | name = "test-record" 22 | records = ["192.168.10.10", "192.168.10.11"] 23 | type = "A" 24 | } 25 | 26 | resource "freeipa_dns_record" "record-7" { 27 | zone_name = "record.com.ua." 28 | name = "test-record" 29 | records = ["2 1 84DE37B22918F76ED66910B47EB440B0A35F4A56"] 30 | type = "SSHFP" 31 | } 32 | ``` 33 | 34 | 35 | 36 | 37 | 38 | ## Schema 39 | 40 | ### Required 41 | 42 | - `name` (String) Record name 43 | - `records` (Set of String) A string list of records 44 | - `type` (String) The record type (A, AAAA, CNAME, MX, PTR, SRV, TXT, SSHP) 45 | - `zone_name` (String) Zone name (FQDN) 46 | 47 | ### Optional 48 | 49 | - `set_identifier` (String) Unique identifier to differentiate records with routing policies from one another 50 | - `ttl` (Number) Time to live 51 | 52 | ### Read-Only 53 | 54 | - `id` (String) ID of the resource 55 | -------------------------------------------------------------------------------- /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 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_dns_zone" "dns_zone-2" { 15 | zone_name = "test.roman.com.ua" 16 | skip_overlap_check = true 17 | disable_zone = false 18 | } 19 | ``` 20 | 21 | 22 | 23 | 24 | 25 | ## Schema 26 | 27 | ### Required 28 | 29 | - `zone_name` (String) Zone name (FQDN) 30 | 31 | ### Optional 32 | 33 | - `admin_email_address` (String) Administrator e-mail address 34 | - `allow_inline_dnssec_signing` (Boolean) Allow inline DNSSEC signing of records in the zone 35 | - `allow_prt_sync` (Boolean) Allow synchronization of forward (A, AAAA) and reverse (PTR) records in the zone 36 | - `allow_query` (String) Semicolon separated list of IP addresses or networks which are allowed to issue queries 37 | - `allow_transfer` (String) Semicolon separated list of IP addresses or networks which are allowed to transfer the zone 38 | - `authoritative_nameserver` (String) Authoritative nameserver domain name 39 | - `bind_update_policy` (String) BIND update policy 40 | - `default_ttl` (Number) Time to live for records without explicit TTL definition 41 | - `disable_zone` (Boolean) Allow disabled the zone 42 | - `dynamic_updates` (Boolean) Allow dynamic updates 43 | - `is_reverse_zone` (Boolean) Allow create the reverse zone 44 | - `nsec3param_record` (String) NSEC3PARAM record for zone in format: hash_algorithm flags iterations salt 45 | - `skip_nameserver_check` (Boolean) Force DNS zone creation even if nameserver is not resolvable 46 | - `skip_overlap_check` (Boolean) Force DNS zone creation even if it will overlap with an existing zone 47 | - `soa_expire` (Number) SOA record expire time 48 | - `soa_minimum` (Number) How long should negative responses be cached 49 | - `soa_refresh` (Number) SOA record refresh time 50 | - `soa_retry` (Number) SOA record retry time 51 | - `ttl` (Number) Time to live for records at zone apex 52 | - `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 53 | 54 | ### Read-Only 55 | 56 | - `computed_zone_name` (String) Real zone name compatible with ARPA (ie: `domain.tld.`) 57 | - `id` (String) ID of the resource 58 | -------------------------------------------------------------------------------- /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 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_group" "group-posix" { 15 | name = "test-group" 16 | description = "Test group" 17 | gid_number = "12345789" 18 | } 19 | 20 | resource "freeipa_group" "group-nonposix" { 21 | name = "test-group" 22 | description = "Test group" 23 | nonposix = true 24 | } 25 | 26 | resource "freeipa_group" "group-external" { 27 | name = "test-group" 28 | description = "Test group" 29 | external = true 30 | } 31 | ``` 32 | 33 | 34 | 35 | 36 | 37 | ## Schema 38 | 39 | ### Required 40 | 41 | - `name` (String) Group name 42 | 43 | - The name must not exceed 32 characters. 44 | - The name must contain only lowercase letters (a-z), digits (0-9), and the characters (. - _). 45 | - The name must not start with a special character. 46 | - A user and a group cannot have the same name. 47 | 48 | ### Optional 49 | 50 | - `addattr` (List of String) Add an attribute/value pair. Format is attr=value. The attribute must be part of the schema. 51 | - `description` (String) Group Description 52 | - `external` (Boolean) Allow adding external non-IPA members from trusted domains 53 | - `gid_number` (Number) GID (use this option to set it manually) 54 | - `nonposix` (Boolean) Create as a non-POSIX group 55 | - `setattr` (List of String) Set an attribute to a name/value pair. Format is attr=value. 56 | 57 | ### Read-Only 58 | 59 | - `id` (String) ID of the resource 60 | -------------------------------------------------------------------------------- /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 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_hbac_policy" "hbac-0" { 15 | name = "test-hbac" 16 | description = "Test HBAC policy" 17 | enabled = true 18 | hostcategory = "all" 19 | servicecategory = "all" 20 | } 21 | ``` 22 | 23 | 24 | 25 | 26 | 27 | ## Schema 28 | 29 | ### Required 30 | 31 | - `name` (String) Name of the hbac policy 32 | 33 | ### Optional 34 | 35 | - `description` (String) HBAC policy description 36 | - `enabled` (Boolean) Enable this hbac policy 37 | - `hostcategory` (String) Host category the hbac policy is applied to (allowed value: all) 38 | - `servicecategory` (String) Service category the hbac policy is applied to (allowed value: all) 39 | - `usercategory` (String) User category the hbac policy is applied to (allowed value: all) 40 | 41 | ### Read-Only 42 | 43 | - `id` (String) ID of the resource 44 | -------------------------------------------------------------------------------- /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 | --- 6 | 7 | # freeipa_hbac_policy_host_membership (Resource) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_hbac_policy" "hbac-0" { 15 | name = "test-hbac" 16 | description = "Test HBAC policy" 17 | enabled = true 18 | } 19 | 20 | resource "freeipa_hbac_policy_host_membership" "hbac-host-1" { 21 | name = "test-hbac" 22 | host = "ipaclient1.ipatest.lan" 23 | } 24 | 25 | resource "freeipa_hbac_policy_host_membership" "hbac-hosts-1" { 26 | name = "test-hbac" 27 | hosts = ["ipaclient1.ipatest.lan", "ipaclient2.ipatest.lan"] 28 | identifier = "hbac-hosts-1" 29 | } 30 | 31 | resource "freeipa_hbac_policy_host_membership" "hostgroup-3" { 32 | name = "test-hbac" 33 | hostgroup = "test-hostgroup" 34 | } 35 | 36 | resource "freeipa_hbac_policy_host_membership" "hostgroups-3" { 37 | name = "test-hbac" 38 | hostgroups = ["test-hostgroup", "test-hostgroup-2"] 39 | identifier = "hostgroups-3" 40 | } 41 | ``` 42 | 43 | 44 | 45 | 46 | 47 | ## Schema 48 | 49 | ### Required 50 | 51 | - `name` (String) HBAC policy name 52 | 53 | ### Optional 54 | 55 | - `host` (String, Deprecated) **deprecated** Host to add to the HBAC policy 56 | - `hostgroup` (String, Deprecated) **deprecated** Hostgroup to add to the HBAC policy 57 | - `hostgroups` (List of String) List of hostgroups to add to the HBAC policy 58 | - `hosts` (List of String) List of hosts to add to the HBAC policy 59 | - `identifier` (String) Unique identifier to differentiate multiple HBAC policy host membership resources on the same HBAC policy. Manadatory for using hosts/hostgroups configurations. 60 | 61 | ### Read-Only 62 | 63 | - `id` (String) ID of the resource 64 | -------------------------------------------------------------------------------- /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 | --- 6 | 7 | # freeipa_hbac_policy_service_membership (Resource) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_hbac_policy" "hbac-0" { 15 | name = "test-hbac" 16 | description = "Test HBAC policy" 17 | enabled = true 18 | hostcategory = "all" 19 | servicecategory = "all" 20 | } 21 | 22 | resource "freeipa_hbac_policy_service_membership" "hbac-svc-1" { 23 | name = "test-hbac" 24 | service = "sshd" 25 | } 26 | 27 | resource "freeipa_hbac_policy_service_membership" "hbac-svc-2" { 28 | name = "test-hbac" 29 | services = ["sshd"] 30 | identifier = "hbac-svc-2" 31 | } 32 | 33 | resource "freeipa_hbac_policy_service_membership" "hbac-svcgrp-1" { 34 | name = "test-hbac" 35 | servicegroup = "Sudo" 36 | } 37 | 38 | resource "freeipa_hbac_policy_service_membership" "hbac-svcgrp-2" { 39 | name = "test-hbac" 40 | servicegroups = ["Sudo", "ftp"] 41 | identifier = "hbac-svcgrp-2" 42 | } 43 | ``` 44 | 45 | 46 | 47 | 48 | 49 | ## Schema 50 | 51 | ### Required 52 | 53 | - `name` (String) HBAC policy name 54 | 55 | ### Optional 56 | 57 | - `identifier` (String) Unique identifier to differentiate multiple HBAC policy service membership resources on the same HBAC policy. Manadatory for using services/servicegroups configurations. 58 | - `service` (String, Deprecated) **deprecated** Service name the policy is applied t 59 | - `servicegroup` (String, Deprecated) **deprecated** Service group name the policy is applied to 60 | - `servicegroups` (List of String) List of service group name the policy is applied to 61 | - `services` (List of String) List of service name the policy is applied t 62 | 63 | ### Read-Only 64 | 65 | - `id` (String) ID of the resource 66 | -------------------------------------------------------------------------------- /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 | --- 6 | 7 | # freeipa_hbac_policy_user_membership (Resource) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_hbac_policy" "hbac-0" { 15 | name = "test-hbac" 16 | description = "Test HBAC policy" 17 | enabled = true 18 | hostcategory = "all" 19 | servicecategory = "all" 20 | } 21 | 22 | resource "freeipa_hbac_policy_user_membership" "hbac-user-1" { 23 | name = "test-hbac" 24 | user = "user-1" 25 | } 26 | 27 | resource "freeipa_hbac_policy_user_membership" "hbac-users-1" { 28 | name = "test-hbac" 29 | users = ["user-2", "user-3"] 30 | identifier = "hbac-users-1" 31 | } 32 | 33 | resource "freeipa_hbac_policy_user_membership" "hbac-group-1" { 34 | name = "test-hbac" 35 | group = "usergroup-1" 36 | } 37 | 38 | resource "freeipa_hbac_policy_user_membership" "hbac-groups-1" { 39 | name = "test-hbac" 40 | groups = ["usergroup-2", "usergroup-3"] 41 | identifier = "hbac-groups-1" 42 | } 43 | ``` 44 | 45 | 46 | 47 | 48 | 49 | ## Schema 50 | 51 | ### Required 52 | 53 | - `name` (String) HBAC policy name 54 | 55 | ### Optional 56 | 57 | - `group` (String, Deprecated) **deprecated** User group to add to the HBAC policy 58 | - `groups` (List of String) List of user groups to add to the HBAC policy 59 | - `identifier` (String) Unique identifier to differentiate multiple HBAC policy user membership resources on the same HBAC policy. Manadatory for using users/groups configurations. 60 | - `user` (String, Deprecated) **deprecated** User FDQN the policy is applied to 61 | - `users` (List of String) List of user FQDNs to add to the HBAC policy 62 | 63 | ### Read-Only 64 | 65 | - `id` (String) ID of the resource 66 | -------------------------------------------------------------------------------- /docs/resources/host.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_host Resource - freeipa" 3 | description: |- 4 | FreeIPA User resource 5 | --- 6 | 7 | # freeipa_host (Resource) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_host" "host-1" { 15 | name = "host-1.example.test" 16 | ip_address = "192.168.1.65" 17 | description = "FreeIPA client in example.test domain" 18 | mac_addresses = ["00:00:00:AA:AA:AA", "00:00:00:BB:BB:BB"] 19 | } 20 | ``` 21 | 22 | 23 | 24 | 25 | 26 | ## Schema 27 | 28 | ### Required 29 | 30 | - `ip_address` (String) IP address of the host 31 | - `name` (String) Host fully qualified name 32 | 33 | - May contain only letters, numbers, '-'. 34 | - DNS label may not start or end with '-' 35 | 36 | ### Optional 37 | 38 | - `assigned_idview` (String) Assigned ID View 39 | - `description` (String) A description of this host 40 | - `force` (Boolean) Skip host's DNS check (A/AAAA) before adding it 41 | - `ipasshpubkeys` (List of String) SSH public keys 42 | - `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. 43 | - `krb_preauth` (Boolean) Pre-authentication is required for the service 44 | - `locality` (String) Host locality (e.g. 'Baltimore, MD') 45 | - `location` (String) Host location (e.g. 'Lab 2') 46 | - `mac_addresses` (List of String) Hardware MAC address(es) on this host 47 | - `operating_system` (String) Host operating system and version (e.g. 'Fedora 40') 48 | - `platform` (String) Host hardware platform (e.g. 'Lenovo T61') 49 | - `random_password` (Boolean) Generate a random password to be used in bulk enrollment 50 | - `trusted_for_delegation` (Boolean) Client credentials may be delegated to the service 51 | - `trusted_to_auth_as_delegate` (Boolean) The service is allowed to authenticate on behalf of a client 52 | - `user_certificates` (List of String) Base-64 encoded host certificate 53 | - `userclass` (List of String) Host category (semantics placed on this attribute are for local interpretation) 54 | - `userpassword` (String, Sensitive) Password used in bulk enrollment 55 | 56 | ### Read-Only 57 | 58 | - `generated_password` (String, Sensitive) Generated random password created at host creation 59 | - `id` (String) ID of the resource 60 | -------------------------------------------------------------------------------- /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 | --- 6 | 7 | # freeipa_host_hostgroup_membership (Resource) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_host_hostgroup_membership" "test-0" { 15 | name = "test-hostgroup-2" 16 | host = "test.example.test" 17 | } 18 | 19 | resource "freeipa_host_hostgroup_membership" "test-1" { 20 | name = "test-hostgroup-2" 21 | hostgroup = "test-hostgroup" 22 | } 23 | 24 | resource "freeipa_host_hostgroup_membership" "test-2" { 25 | name = "test-hostgroup-2" 26 | hosts = ["host1", "host2"] 27 | hostgroups = ["test-hostgroup", "test-hostgroup2"] 28 | identifier = "my_unique_identifier" 29 | } 30 | ``` 31 | 32 | 33 | 34 | 35 | 36 | ## Schema 37 | 38 | ### Required 39 | 40 | - `name` (String) Hostgroup name 41 | 42 | ### Optional 43 | 44 | - `host` (String, Deprecated) **deprecated** Host to add. Will be replaced by hosts. 45 | - `hostgroup` (String, Deprecated) **deprecated** Hostgroup to add. Will be replaced by hostgroups. 46 | - `hostgroups` (List of String) Hostgroups to add as hostgroup members 47 | - `hosts` (List of String) Hosts to add as hostgroup members 48 | - `identifier` (String) Unique identifier to differentiate multiple hostgroup membership resources on the same hostgroup. Manadatory for using hosts/hostgroups configurations. 49 | 50 | ### Read-Only 51 | 52 | - `id` (String) ID of the resource 53 | -------------------------------------------------------------------------------- /docs/resources/hostgroup.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_hostgroup Resource - freeipa" 3 | description: |- 4 | FreeIPA User Group resource 5 | --- 6 | 7 | # freeipa_hostgroup (Resource) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_hostgroup" "hostgroup-1" { 15 | name = "hostgroup-1" 16 | description = "FreeIPA hostgroup" 17 | } 18 | ``` 19 | 20 | 21 | 22 | 23 | 24 | ## Schema 25 | 26 | ### Required 27 | 28 | - `name` (String) Hostgroup name 29 | 30 | ### Optional 31 | 32 | - `description` (String) Hostgroup Description 33 | 34 | ### Read-Only 35 | 36 | - `id` (String) ID of the resource 37 | -------------------------------------------------------------------------------- /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 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_sudo_cmd" "bash" { 15 | name = "/bin/bash" 16 | description = "The bash terminal" 17 | } 18 | ``` 19 | 20 | 21 | 22 | 23 | 24 | ## Schema 25 | 26 | ### Required 27 | 28 | - `name` (String) Absolute path of the sudo command (case sensitive) 29 | 30 | ### Optional 31 | 32 | - `description` (String) Sudo command description 33 | 34 | ### Read-Only 35 | 36 | - `id` (String) ID of the resource 37 | -------------------------------------------------------------------------------- /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 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_sudo_cmdgroup" "service_management" { 15 | name = "service-management" 16 | description = "Service management related sudo commands" 17 | } 18 | ``` 19 | 20 | 21 | 22 | 23 | 24 | ## Schema 25 | 26 | ### Required 27 | 28 | - `name` (String) Name of the sudo command group 29 | 30 | ### Optional 31 | 32 | - `description` (String) Sudo command group description 33 | 34 | ### Read-Only 35 | 36 | - `id` (String) ID of the resource 37 | -------------------------------------------------------------------------------- /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 | --- 6 | 7 | # freeipa_sudo_cmdgroup_membership (Resource) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_sudo_cmd" "bash" { 15 | name = "/bin/bash" 16 | description = "The bash shell" 17 | } 18 | 19 | resource "freeipa_sudo_cmd" "fish" { 20 | name = "/bin/fish" 21 | description = "The fish shell" 22 | } 23 | 24 | resource "freeipa_sudo_cmdgroup" "terminals" { 25 | name = "terminals" 26 | description = "The terminals allowed to be sudoed" 27 | } 28 | 29 | resource "freeipa_sudo_cmdgroup_membership" "terminal_bash" { 30 | name = freeipa_sudo_cmdgroup.terminals.id 31 | sudocmd = freeipa_sudo_cmd.bash.id 32 | } 33 | 34 | resource "freeipa_sudo_cmdgroup_membership" "terminal_fish" { 35 | name = freeipa_sudo_cmdgroup.terminals.id 36 | sudocmd = freeipa_sudo_cmd.fish.id 37 | } 38 | ``` 39 | 40 | 41 | 42 | 43 | 44 | ## Schema 45 | 46 | ### Required 47 | 48 | - `name` (String) Name of the sudo command group 49 | 50 | ### Optional 51 | 52 | - `identifier` (String) Unique identifier to differentiate multiple sudo command group membership resources on the same sudo command group. Manadatory for using sudocmds configurations. 53 | - `sudocmd` (String, Deprecated) **deprecated** Sudo command to add as a member 54 | - `sudocmds` (List of String) List of sudo command to add as a member 55 | 56 | ### Read-Only 57 | 58 | - `id` (String) ID of the resource 59 | -------------------------------------------------------------------------------- /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 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_sudo_rule" "sysadmins" { 15 | name = "sysadmins" 16 | description = "Sysadmins have all permissions" 17 | } 18 | ``` 19 | 20 | 21 | 22 | 23 | 24 | ## Schema 25 | 26 | ### Required 27 | 28 | - `name` (String) Name of the sudo rule 29 | 30 | ### Optional 31 | 32 | - `commandcategory` (String) Command category the sudo rule is applied to (allowed value: all) 33 | - `description` (String) Sudo rule description 34 | - `enabled` (Boolean) Enable this sudo rule 35 | - `hostcategory` (String) Host category the sudo rule is applied to (allowed value: all) 36 | - `order` (Number) Sudo rule order (must be unique) 37 | - `runasgroupcategory` (String) Run as group category the sudo rule is applied to (allowed value: all) 38 | - `runasusercategory` (String) Run as user category the sudo rule is applied to (allowed value: all) 39 | - `usercategory` (String) User category the sudo rule is applied to (allowed value: all) 40 | 41 | ### Read-Only 42 | 43 | - `id` (String) ID of the resource 44 | -------------------------------------------------------------------------------- /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 | --- 6 | 7 | # freeipa_sudo_rule_allowcmd_membership (Resource) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_sudo_rule_allowcmd_membership" "allowed_cmd" { 15 | name = "sudo-rule-editors" 16 | sudocmd = "/bin/bash" 17 | } 18 | 19 | resource "freeipa_sudo_rule_allowcmd_membership" "allowed_cmd" { 20 | name = "sudo-rule-editors" 21 | sudocmds = ["/bin/bash"] 22 | identifier = "allowed_bash" 23 | } 24 | 25 | resource "freeipa_sudo_rule_allowcmd_membership" "allowed_cmdgrp" { 26 | name = "sudo-rule-editors" 27 | sudocmd_group = "allowed-terminals" 28 | } 29 | 30 | resource "freeipa_sudo_rule_allowcmd_membership" "allowed_cmdgrp" { 31 | name = "sudo-rule-editors" 32 | sudocmd_groups = ["allowed-terminals"] 33 | identifier = "allowed_terminals" 34 | } 35 | ``` 36 | 37 | 38 | 39 | 40 | 41 | ## Schema 42 | 43 | ### Required 44 | 45 | - `name` (String) Sudo rule name 46 | 47 | ### Optional 48 | 49 | - `identifier` (String) Unique identifier to differentiate multiple sudo rule denied membership resources on the same sudo rule. Manadatory for using sudocmds/sudocmd_groups configurations. 50 | - `sudocmd` (String, Deprecated) **deprecated** Sudo command to allow by the sudo rule 51 | - `sudocmd_group` (String, Deprecated) **deprecated** Sudo command group to allow by the sudo rule 52 | - `sudocmd_groups` (List of String) List of sudo command group to allow by the sudo rule 53 | - `sudocmds` (List of String) List of Sudo command to allow by the sudo rule 54 | 55 | ### Read-Only 56 | 57 | - `id` (String) ID of the resource 58 | -------------------------------------------------------------------------------- /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 | --- 6 | 7 | # freeipa_sudo_rule_denycmd_membership (Resource) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_sudo_rule_denycmd_membership" "denied_cmd" { 15 | name = "sudo-rule-restricted" 16 | sudocmd = "/usr/bin/systemctl" 17 | } 18 | 19 | resource "freeipa_sudo_rule_denycmd_membership" "denied_cmd" { 20 | name = "sudo-rule-restricted" 21 | sudocmds = ["/usr/bin/systemctl"] 22 | identifier = "denied_systemctl" 23 | } 24 | 25 | resource "freeipa_sudo_rule_denycmd_membership" "denied_cmdgrp" { 26 | name = "sudo-rule-restricted" 27 | sudocmd_group = "service-management" 28 | } 29 | 30 | resource "freeipa_sudo_rule_denycmd_membership" "denied_cmdgrp" { 31 | name = "sudo-rule-restricted" 32 | sudocmd_groups = ["service-management"] 33 | identifier = "denied_services" 34 | } 35 | ``` 36 | 37 | 38 | 39 | 40 | 41 | ## Schema 42 | 43 | ### Required 44 | 45 | - `name` (String) Sudo rule name 46 | 47 | ### Optional 48 | 49 | - `identifier` (String) Unique identifier to differentiate multiple sudo rule allowed membership resources on the same sudo rule. Manadatory for using sudocmds/sudocmd_groups configurations. 50 | - `sudocmd` (String, Deprecated) **deprecated** Sudo command to deny by the sudo rule 51 | - `sudocmd_group` (String, Deprecated) **deprecated** Sudo command group to deny by the sudo rule 52 | - `sudocmd_groups` (List of String) List of sudo command group to deny by the sudo rule 53 | - `sudocmds` (List of String) List of Sudo command to deny by the sudo rule 54 | 55 | ### Read-Only 56 | 57 | - `id` (String) ID of the resource 58 | -------------------------------------------------------------------------------- /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 | --- 6 | 7 | # freeipa_sudo_rule_host_membership (Resource) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_sudo_rule_host_membership" "host-0" { 15 | name = "sudo-rule-test" 16 | host = "test.example.test" 17 | } 18 | 19 | resource "freeipa_sudo_rule_host_membership" "hosts-0" { 20 | name = "sudo-rule-test" 21 | hosts = ["test.example.test"] 22 | identifier = "hosts-0" 23 | } 24 | 25 | resource "freeipa_sudo_rule_host_membership" "hostgroup-3" { 26 | name = "sudo-rule-test" 27 | hostgroup = "test-hostgroup" 28 | } 29 | 30 | resource "freeipa_sudo_rule_host_membership" "hostgroups-3" { 31 | name = "sudo-rule-test" 32 | hostgroups = ["test-hostgroup"] 33 | identifier = "hostgroups-3" 34 | } 35 | ``` 36 | 37 | 38 | 39 | 40 | 41 | ## Schema 42 | 43 | ### Required 44 | 45 | - `name` (String) Sudo rule name 46 | 47 | ### Optional 48 | 49 | - `host` (String, Deprecated) **deprecated** Host to add to the sudo rule 50 | - `hostgroup` (String, Deprecated) **deprecated** Hostgroup to add to the sudo rule 51 | - `hostgroups` (List of String) List of hostgroups to add to the sudo rule 52 | - `hosts` (List of String) List of hosts to add to the sudo rule 53 | - `identifier` (String) Unique identifier to differentiate multiple sudo rule host membership resources on the same sudo rule. Manadatory for using hosts/hostgroups configurations. 54 | 55 | ### Read-Only 56 | 57 | - `id` (String) ID of the resource 58 | -------------------------------------------------------------------------------- /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 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_sudo_rule_option" "option-0" { 15 | name = "sudo-rule-test" 16 | option = "!authenticate" 17 | } 18 | ``` 19 | 20 | 21 | 22 | 23 | 24 | ## Schema 25 | 26 | ### Required 27 | 28 | - `name` (String) Sudo rule name 29 | - `option` (String) Sudo option to add to the sudo rule. 30 | 31 | ### Read-Only 32 | 33 | - `id` (String) ID of the resource 34 | -------------------------------------------------------------------------------- /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 | --- 6 | 7 | # freeipa_sudo_rule_runasgroup_membership (Resource) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_sudo_rule_runasgroup_membership" "group-0" { 15 | name = "sudo-rule-test" 16 | runasgroup = "group01" 17 | } 18 | 19 | resource "freeipa_sudo_rule_runasgroup_membership" "groups-0" { 20 | name = "sudo-rule-test" 21 | runasgroups = ["group01", "group02"] 22 | identifier = "groups-0" 23 | } 24 | ``` 25 | 26 | 27 | 28 | 29 | 30 | ## Schema 31 | 32 | ### Required 33 | 34 | - `name` (String) Sudo rule name 35 | 36 | ### Optional 37 | 38 | - `identifier` (String) Unique identifier to differentiate multiple sudo rule runasgroup membership resources on the same sudo rule. Manadatory for using runasgroups configurations. 39 | - `runasgroup` (String, Deprecated) **deprecated** Run As Group to add to the sudo rule. Can be an external group (local group of ipa clients) 40 | - `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) 41 | 42 | ### Read-Only 43 | 44 | - `id` (String) ID of the resource 45 | -------------------------------------------------------------------------------- /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 | --- 6 | 7 | # freeipa_sudo_rule_runasuser_membership (Resource) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_sudo_rule_runasuser_membership" "user-0" { 15 | name = "sudo-rule-test" 16 | runasuser = "user01" 17 | } 18 | 19 | resource "freeipa_sudo_rule_runasuser_membership" "users-0" { 20 | name = "sudo-rule-test" 21 | runasusers = ["user01", "user02"] 22 | identifier = "users-0" 23 | } 24 | ``` 25 | 26 | 27 | 28 | 29 | 30 | ## Schema 31 | 32 | ### Required 33 | 34 | - `name` (String) Sudo rule name 35 | 36 | ### Optional 37 | 38 | - `identifier` (String) Unique identifier to differentiate multiple sudo rule runasuser membership resources on the same sudo rule. Manadatory for using runasusers configurations. 39 | - `runasuser` (String, Deprecated) **deprecated** Run As User to add to the sudo rule. Can be an external user (local user of ipa clients) 40 | - `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) 41 | 42 | ### Read-Only 43 | 44 | - `id` (String) ID of the resource 45 | -------------------------------------------------------------------------------- /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 | --- 6 | 7 | # freeipa_sudo_rule_user_membership (Resource) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_sudo_rule_user_membership" "user-0" { 15 | name = "sudo-rule-test" 16 | user = "user01" 17 | } 18 | 19 | resource "freeipa_sudo_rule_user_membership" "users-1" { 20 | name = "sudo-rule-test" 21 | users = ["user01"] 22 | identifier = "users-1" 23 | } 24 | 25 | resource "freeipa_sudo_rule_user_membership" "group-3" { 26 | name = "sudo-rule-test" 27 | group = "test-group-0" 28 | } 29 | 30 | resource "freeipa_sudo_rule_user_membership" "groups-3" { 31 | name = "sudo-rule-test" 32 | groups = ["test-group-0"] 33 | identifier = "groups-3" 34 | } 35 | ``` 36 | 37 | 38 | 39 | 40 | 41 | ## Schema 42 | 43 | ### Required 44 | 45 | - `name` (String) Sudo rule name 46 | 47 | ### Optional 48 | 49 | - `group` (String, Deprecated) **deprecated** User group to add to the sudo rule 50 | - `groups` (List of String) List of user groups to add to the sudo rule 51 | - `identifier` (String) Unique identifier to differentiate multiple sudo rule user membership resources on the same sudo rule. Manadatory for using users/groups configurations. 52 | - `user` (String, Deprecated) **deprecated** User to add to the sudo rule 53 | - `users` (List of String) List of users to add to the sudo rule 54 | 55 | ### Read-Only 56 | 57 | - `id` (String) ID of the resource 58 | -------------------------------------------------------------------------------- /docs/resources/user.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "freeipa_user Resource - freeipa" 3 | description: |- 4 | FreeIPA User resource 5 | --- 6 | 7 | # freeipa_user (Resource) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_user" "user-1" { 15 | first_name = "Roman" 16 | last_name = "Roman" 17 | name = "roman" 18 | telephone_numbers = ["+380982555429", "2-10-11"] 19 | email_address = ["roman@example.com"] 20 | } 21 | ``` 22 | 23 | 24 | 25 | 26 | 27 | ## Schema 28 | 29 | ### Required 30 | 31 | - `first_name` (String) First name 32 | - `last_name` (String) Last name 33 | - `name` (String) UID or Login 34 | 35 | - The name must not exceed 32 characters. 36 | - The name must contain only lowercase letters (a-z), digits (0-9), and the characters (. - _). 37 | - The name must not start with a special character. 38 | - A user and a group cannot have the same name. 39 | 40 | ### Optional 41 | 42 | - `account_disabled` (Boolean) Account disabled 43 | - `car_license` (List of String) Car Licenses 44 | - `city` (String) City 45 | - `display_name` (String) Display name 46 | - `email_address` (List of String) Email address 47 | - `employee_number` (String) Employee Number 48 | - `employee_type` (String) Employee Type 49 | - `full_name` (String) Full name 50 | - `gecos` (String) GECOS 51 | - `gid_number` (Number) Group ID Number 52 | - `home_directory` (String) Home Directory 53 | - `initials` (String) Initials 54 | - `job_title` (String) Job Title 55 | - `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`) 56 | - `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`) 57 | - `krb_principal_name` (List of String) Principal alias 58 | - `login_shell` (String) Login Shell 59 | - `manager` (String) Manager 60 | - `mobile_numbers` (List of String) Mobile Number 61 | - `organisation_unit` (String) Org. Unit 62 | - `postal_code` (String) Postal code 63 | - `preferred_language` (String) Preferred Language 64 | - `province` (String) Province/State/Country 65 | - `random_password` (Boolean) Generate a random user password 66 | - `ssh_public_key` (List of String) List of SSH public keys 67 | - `street_address` (String) Street address 68 | - `telephone_numbers` (List of String) Telephone Number 69 | - `uid_number` (Number) User ID Number (system will assign one if not provided) 70 | - `userclass` (List of String) User category (semantics placed on this attribute are for local interpretation) 71 | - `userpassword` (String, Sensitive) Prompt to set the user password 72 | 73 | ### Read-Only 74 | 75 | - `id` (String) ID of the resource 76 | -------------------------------------------------------------------------------- /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 | --- 6 | 7 | # freeipa_user_group_membership (Resource) 8 | 9 | 10 | 11 | ## Example Usage 12 | 13 | ```terraform 14 | resource "freeipa_user_group_membership" "test-0" { 15 | name = "test-group-2" 16 | user = "roman" 17 | } 18 | 19 | resource "freeipa_user_group_membership" "test-1" { 20 | name = "test-group-2" 21 | group = "test-group" 22 | } 23 | 24 | resource "freeipa_user_group_membership" "test-2" { 25 | name = "test-group-2" 26 | external_member = "domain users@adtest.lan" 27 | } 28 | 29 | resource "freeipa_user_group_membership" "test-3" { 30 | name = "test-group-3" 31 | users = ["user1", "user2"] 32 | groups = ["group1", "group2"] 33 | identifier = "my_unique_identifier" 34 | } 35 | ``` 36 | 37 | 38 | 39 | 40 | 41 | ## Schema 42 | 43 | ### Required 44 | 45 | - `name` (String) Group name 46 | 47 | ### Optional 48 | 49 | - `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. 50 | - `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). 51 | - `group` (String, Deprecated) **deprecated** User group to add. Will be replaced by groups. 52 | - `groups` (List of String) User groups to add as group members 53 | - `identifier` (String) Unique identifier to differentiate multiple user group membership resources on the same group. Manadatory for using users/groups/external_members configurations. 54 | - `user` (String, Deprecated) **deprecated** User to add. Will be replaced by users. 55 | - `users` (List of String) Users to add as group members 56 | 57 | ### Read-Only 58 | 59 | - `id` (String) ID of the resource 60 | -------------------------------------------------------------------------------- /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/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/data-sources/freeipa_group/data-source.tf: -------------------------------------------------------------------------------- 1 | data "freeipa_group" "group-0" { 2 | name = "test-group" 3 | } -------------------------------------------------------------------------------- /examples/data-sources/freeipa_hbac_policy/data-source.tf: -------------------------------------------------------------------------------- 1 | data "freeipa_hbac_policy" "myservers" { 2 | name = "myservers" 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_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/data-sources/freeipa_sudo_rule/data-source.tf: -------------------------------------------------------------------------------- 1 | data "freeipa_sudo_rule" "operators" { 2 | name = "operators" 3 | } -------------------------------------------------------------------------------- /examples/data-sources/freeipa_user/data-source.tf: -------------------------------------------------------------------------------- 1 | data "freeipa_user" "user-0" { 2 | name = "test-user" 3 | first_name = "John" 4 | last_name = "Doe" 5 | } -------------------------------------------------------------------------------- /examples/provider/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | freeipa = { 4 | version = "0.1.1" 5 | source = "[Terraform registry provider path]" 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_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_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_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_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 | } -------------------------------------------------------------------------------- /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_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 | -------------------------------------------------------------------------------- /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_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_hostgroup/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_hostgroup" "hostgroup-1" { 2 | name = "hostgroup-1" 3 | description = "FreeIPA hostgroup" 4 | } 5 | -------------------------------------------------------------------------------- /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_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/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/resource.tf: -------------------------------------------------------------------------------- 1 | resource "freeipa_sudo_rule" "sysadmins" { 2 | name = "sysadmins" 3 | description = "Sysadmins have all permissions" 4 | } 5 | -------------------------------------------------------------------------------- /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_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 | } -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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_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 | } -------------------------------------------------------------------------------- /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_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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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_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 | -------------------------------------------------------------------------------- /freeipa/hostgroup_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/path" 22 | "github.com/hashicorp/terraform-plugin-framework/resource" 23 | "github.com/hashicorp/terraform-plugin-framework/resource/schema" 24 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" 25 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" 26 | "github.com/hashicorp/terraform-plugin-framework/types" 27 | "github.com/hashicorp/terraform-plugin-log/tflog" 28 | ipa "github.com/infra-monkey/go-freeipa/freeipa" 29 | ) 30 | 31 | // Ensure provider defined types fully satisfy framework interfaces. 32 | var _ resource.Resource = &HostGroupResource{} 33 | var _ resource.ResourceWithImportState = &HostGroupResource{} 34 | 35 | func NewHostGroupResource() resource.Resource { 36 | return &HostGroupResource{} 37 | } 38 | 39 | // HostGroupResource defines the resource implementation. 40 | type HostGroupResource struct { 41 | client *ipa.Client 42 | } 43 | 44 | // HostGroupResourceModel describes the resource data model. 45 | type HostGroupResourceModel struct { 46 | Id types.String `tfsdk:"id"` 47 | Name types.String `tfsdk:"name"` 48 | Description types.String `tfsdk:"description"` 49 | } 50 | 51 | func (r *HostGroupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { 52 | resp.TypeName = req.ProviderTypeName + "_hostgroup" 53 | } 54 | 55 | func (r *HostGroupResource) ConfigValidators(ctx context.Context) []resource.ConfigValidator { 56 | return []resource.ConfigValidator{} 57 | } 58 | 59 | func (r *HostGroupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { 60 | resp.Schema = schema.Schema{ 61 | // This description is used by the documentation generator and the language server. 62 | MarkdownDescription: "FreeIPA User Group resource", 63 | 64 | Attributes: map[string]schema.Attribute{ 65 | "id": schema.StringAttribute{ 66 | MarkdownDescription: "ID of the resource", 67 | Computed: true, 68 | PlanModifiers: []planmodifier.String{ 69 | stringplanmodifier.UseStateForUnknown(), 70 | }, 71 | }, 72 | "name": schema.StringAttribute{ 73 | MarkdownDescription: "Hostgroup name", 74 | Required: true, 75 | PlanModifiers: []planmodifier.String{ 76 | stringplanmodifier.RequiresReplace(), 77 | }, 78 | }, 79 | "description": schema.StringAttribute{ 80 | MarkdownDescription: "Hostgroup Description", 81 | Optional: true, 82 | }, 83 | }, 84 | } 85 | } 86 | 87 | func (r *HostGroupResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { 88 | // Prevent panic if the provider has not been configured. 89 | if req.ProviderData == nil { 90 | return 91 | } 92 | 93 | client, ok := req.ProviderData.(*ipa.Client) 94 | 95 | if !ok { 96 | resp.Diagnostics.AddError( 97 | "Unexpected Resource Configure Type", 98 | fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), 99 | ) 100 | 101 | return 102 | } 103 | 104 | r.client = client 105 | } 106 | 107 | func (r *HostGroupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { 108 | var data HostGroupResourceModel 109 | 110 | // Read Terraform plan data into the model 111 | resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) 112 | 113 | if resp.Diagnostics.HasError() { 114 | return 115 | } 116 | 117 | optArgs := ipa.HostgroupAddOptionalArgs{} 118 | 119 | args := ipa.HostgroupAddArgs{ 120 | Cn: data.Name.ValueString(), 121 | } 122 | if !data.Description.IsNull() { 123 | optArgs.Description = data.Description.ValueStringPointer() 124 | } 125 | tflog.Trace(ctx, "created a host group resource") 126 | 127 | res, err := r.client.HostgroupAdd(&args, &optArgs) 128 | if err != nil { 129 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error creating freeipa host group: %s", err)) 130 | } 131 | 132 | data.Id = types.StringValue(res.Result.Cn) 133 | if resp.Diagnostics.HasError() { 134 | return 135 | } 136 | 137 | // Save data into Terraform state 138 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 139 | } 140 | 141 | func (r *HostGroupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { 142 | var data HostGroupResourceModel 143 | 144 | // Read Terraform prior state data into the model 145 | resp.Diagnostics.Append(req.State.Get(ctx, &data)...) 146 | 147 | if resp.Diagnostics.HasError() { 148 | return 149 | } 150 | 151 | all := true 152 | optArgs := ipa.HostgroupShowOptionalArgs{ 153 | All: &all, 154 | } 155 | 156 | args := ipa.HostgroupShowArgs{ 157 | Cn: data.Name.ValueString(), 158 | } 159 | 160 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Read freeipa hostgroup %s", data.Id.ValueString())) 161 | res, err := r.client.HostgroupShow(&args, &optArgs) 162 | if err != nil { 163 | if strings.Contains(err.Error(), "NotFound") { 164 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Hostgroup %s not found", data.Id.ValueString())) 165 | resp.State.RemoveResource(ctx) 166 | return 167 | } else { 168 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("[DEBUG] Hostgroup %s not found: %s", data.Id.ValueString(), err)) 169 | } 170 | } 171 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Read freeipa hostgroup %v", res)) 172 | if res != nil { 173 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Read freeipa hostgroup %s", res.Result.String())) 174 | } else { 175 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error reading freeipa hostgroup %s", data.Name.ValueString())) 176 | return 177 | } 178 | 179 | //data.Name = types.StringValue(res.Result.Cn) 180 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Read freeipa hostgroup Cn %s", data.Name.ValueString())) 181 | if res.Result.Description != nil && !data.Description.IsNull() { 182 | data.Description = types.StringValue(*res.Result.Description) 183 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Read freeipa hostgroup Description %s", data.Description.ValueString())) 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 *HostGroupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { 194 | var data, state HostGroupResourceModel 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 | args := ipa.HostgroupModArgs{ 205 | Cn: data.Name.ValueString(), 206 | } 207 | optArgs := ipa.HostgroupModOptionalArgs{} 208 | 209 | if !data.Description.Equal(state.Description) { 210 | optArgs.Description = data.Description.ValueStringPointer() 211 | } 212 | res, err := r.client.HostgroupMod(&args, &optArgs) 213 | if err != nil { 214 | if strings.Contains(err.Error(), "EmptyModlist") { 215 | resp.Diagnostics.AddWarning("Client Warning", err.Error()) 216 | } else { 217 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error update freeipa group %s: %s", res.Result.Cn, err)) 218 | return 219 | } 220 | } 221 | 222 | // Save updated data into Terraform state 223 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 224 | } 225 | 226 | func (r *HostGroupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { 227 | var data HostGroupResourceModel 228 | 229 | // Read Terraform prior state data into the model 230 | resp.Diagnostics.Append(req.State.Get(ctx, &data)...) 231 | 232 | if resp.Diagnostics.HasError() { 233 | return 234 | } 235 | 236 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Delete freeipa hostgroup Id %s", data.Id.ValueString())) 237 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Delete freeipa hostgroup Name %s", data.Name.ValueString())) 238 | args := ipa.HostgroupDelArgs{ 239 | Cn: []string{data.Name.ValueString()}, 240 | } 241 | _, err := r.client.HostgroupDel(&args, &ipa.HostgroupDelOptionalArgs{}) 242 | if err != nil { 243 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("[DEBUG] Hostgroup %s deletion failed: %s", data.Id.ValueString(), err)) 244 | return 245 | } 246 | } 247 | 248 | func (r *HostGroupResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { 249 | resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) 250 | } 251 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /freeipa/sudo_cmd_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/path" 22 | "github.com/hashicorp/terraform-plugin-framework/resource" 23 | "github.com/hashicorp/terraform-plugin-framework/resource/schema" 24 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" 25 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" 26 | "github.com/hashicorp/terraform-plugin-framework/types" 27 | "github.com/hashicorp/terraform-plugin-log/tflog" 28 | ipa "github.com/infra-monkey/go-freeipa/freeipa" 29 | ) 30 | 31 | // Ensure provider defined types fully satisfy framework interfaces. 32 | var _ resource.Resource = &SudoCmdResource{} 33 | var _ resource.ResourceWithImportState = &SudoCmdResource{} 34 | 35 | func NewSudoCmdResource() resource.Resource { 36 | return &SudoCmdResource{} 37 | } 38 | 39 | // SudoCmdResource defines the resource implementation. 40 | type SudoCmdResource struct { 41 | client *ipa.Client 42 | } 43 | 44 | // SudoCmdResourceModel describes the resource data model. 45 | type SudoCmdResourceModel struct { 46 | Id types.String `tfsdk:"id"` 47 | Name types.String `tfsdk:"name"` 48 | Description types.String `tfsdk:"description"` 49 | } 50 | 51 | func (r *SudoCmdResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { 52 | resp.TypeName = req.ProviderTypeName + "_sudo_cmd" 53 | } 54 | 55 | func (r *SudoCmdResource) ConfigValidators(ctx context.Context) []resource.ConfigValidator { 56 | return []resource.ConfigValidator{} 57 | } 58 | 59 | func (r *SudoCmdResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { 60 | resp.Schema = schema.Schema{ 61 | // This description is used by the documentation generator and the language server. 62 | MarkdownDescription: "FreeIPA Sudo command resource", 63 | 64 | Attributes: map[string]schema.Attribute{ 65 | "id": schema.StringAttribute{ 66 | MarkdownDescription: "ID of the resource", 67 | Computed: true, 68 | PlanModifiers: []planmodifier.String{ 69 | stringplanmodifier.UseStateForUnknown(), 70 | }, 71 | }, 72 | "name": schema.StringAttribute{ 73 | MarkdownDescription: "Absolute path of the sudo command (case sensitive)", 74 | Required: true, 75 | PlanModifiers: []planmodifier.String{ 76 | stringplanmodifier.RequiresReplace(), 77 | }, 78 | }, 79 | "description": schema.StringAttribute{ 80 | MarkdownDescription: "Sudo command description", 81 | Optional: true, 82 | }, 83 | }, 84 | } 85 | } 86 | 87 | func (r *SudoCmdResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { 88 | // Prevent panic if the provider has not been configured. 89 | if req.ProviderData == nil { 90 | return 91 | } 92 | 93 | client, ok := req.ProviderData.(*ipa.Client) 94 | 95 | if !ok { 96 | resp.Diagnostics.AddError( 97 | "Unexpected Resource Configure Type", 98 | fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), 99 | ) 100 | 101 | return 102 | } 103 | 104 | r.client = client 105 | } 106 | 107 | func (r *SudoCmdResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { 108 | var data SudoCmdResourceModel 109 | 110 | // Read Terraform plan data into the model 111 | resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) 112 | 113 | if resp.Diagnostics.HasError() { 114 | return 115 | } 116 | 117 | optArgs := ipa.SudocmdAddOptionalArgs{} 118 | 119 | args := ipa.SudocmdAddArgs{ 120 | Sudocmd: data.Name.ValueString(), 121 | } 122 | 123 | if !data.Description.IsNull() { 124 | optArgs.Description = data.Description.ValueStringPointer() 125 | } 126 | _, err := r.client.SudocmdAdd(&args, &optArgs) 127 | if err != nil { 128 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error creating freeipa sudo command: %s", err)) 129 | } 130 | data.Id = data.Name 131 | 132 | if resp.Diagnostics.HasError() { 133 | return 134 | } 135 | 136 | // Save data into Terraform state 137 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 138 | } 139 | 140 | func (r *SudoCmdResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { 141 | var data SudoCmdResourceModel 142 | 143 | // Read Terraform prior state data into the model 144 | resp.Diagnostics.Append(req.State.Get(ctx, &data)...) 145 | 146 | if resp.Diagnostics.HasError() { 147 | return 148 | } 149 | 150 | all := true 151 | args := ipa.SudocmdShowArgs{ 152 | Sudocmd: data.Name.ValueString(), 153 | } 154 | optArgs := ipa.SudocmdShowOptionalArgs{ 155 | All: &all, 156 | } 157 | 158 | res, err := r.client.SudocmdShow(&args, &optArgs) 159 | if err != nil { 160 | if strings.Contains(err.Error(), "NotFound") { 161 | tflog.Debug(ctx, "[DEBUG] Sudo command not found") 162 | resp.State.RemoveResource(ctx) 163 | return 164 | } else { 165 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error reading freeipa sudo command: %s", err)) 166 | return 167 | } 168 | } 169 | if res != nil { 170 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Read freeipa sudo command %s", res.Result.String())) 171 | } else { 172 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error reading freeipa sudo command %s", data.Name.ValueString())) 173 | return 174 | } 175 | 176 | if res.Result.Description != nil && !data.Description.IsNull() { 177 | data.Description = types.StringValue(*res.Result.Description) 178 | } 179 | data.Id = data.Name 180 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Read freeipa sudo command %s", res.Result.Sudocmd)) 181 | 182 | // Save updated data into Terraform state 183 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 184 | if resp.Diagnostics.HasError() { 185 | return 186 | } 187 | } 188 | 189 | func (r *SudoCmdResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { 190 | var data, state SudoCmdResourceModel 191 | 192 | // Read Terraform plan data into the model 193 | resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) 194 | resp.Diagnostics.Append(req.State.Get(ctx, &state)...) 195 | 196 | if resp.Diagnostics.HasError() { 197 | return 198 | } 199 | 200 | optArgs := ipa.SudocmdModOptionalArgs{} 201 | 202 | args := ipa.SudocmdModArgs{ 203 | Sudocmd: data.Name.ValueString(), 204 | } 205 | 206 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Update freeipa sudo command %s from plan = %v", data.Name.ValueString(), data)) 207 | if !data.Description.Equal(state.Description) { 208 | if data.Description.ValueStringPointer() != nil { 209 | optArgs.Description = data.Description.ValueStringPointer() 210 | } else { 211 | v := "" 212 | optArgs.Description = &v 213 | } 214 | } 215 | _, err := r.client.SudocmdMod(&args, &optArgs) 216 | if err != nil { 217 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error updating freeipa sudo command: %s", err)) 218 | } 219 | 220 | data.Id = data.Name 221 | 222 | if resp.Diagnostics.HasError() { 223 | return 224 | } 225 | 226 | // Save updated data into Terraform state 227 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 228 | } 229 | 230 | func (r *SudoCmdResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { 231 | var data SudoCmdResourceModel 232 | 233 | // Read Terraform prior state data into the model 234 | resp.Diagnostics.Append(req.State.Get(ctx, &data)...) 235 | 236 | if resp.Diagnostics.HasError() { 237 | return 238 | } 239 | 240 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Delete freeipa sudo command Id %s", data.Id.ValueString())) 241 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Delete freeipa sudo command Name %s", data.Name.ValueString())) 242 | args := ipa.SudocmdDelArgs{ 243 | Sudocmd: []string{data.Name.ValueString()}, 244 | } 245 | optArgs := ipa.SudocmdDelOptionalArgs{} 246 | _, err := r.client.SudocmdDel(&args, &optArgs) 247 | if err != nil { 248 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("[DEBUG] Sudo command %s deletion failed: %s", data.Id.ValueString(), err)) 249 | return 250 | } 251 | 252 | } 253 | 254 | func (r *SudoCmdResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { 255 | resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) 256 | } 257 | -------------------------------------------------------------------------------- /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_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 User 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 | -------------------------------------------------------------------------------- /freeipa/sudo_cmdgroup_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/path" 22 | "github.com/hashicorp/terraform-plugin-framework/resource" 23 | "github.com/hashicorp/terraform-plugin-framework/resource/schema" 24 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" 25 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" 26 | "github.com/hashicorp/terraform-plugin-framework/types" 27 | "github.com/hashicorp/terraform-plugin-log/tflog" 28 | ipa "github.com/infra-monkey/go-freeipa/freeipa" 29 | ) 30 | 31 | // Ensure provider defined types fully satisfy framework interfaces. 32 | var _ resource.Resource = &SudoCmdGroupResource{} 33 | var _ resource.ResourceWithImportState = &SudoCmdGroupResource{} 34 | 35 | func NewSudoCmdGroupResource() resource.Resource { 36 | return &SudoCmdGroupResource{} 37 | } 38 | 39 | // SudoCmdGroupResource defines the resource implementation. 40 | type SudoCmdGroupResource struct { 41 | client *ipa.Client 42 | } 43 | 44 | // SudoCmdGroupResourceModel describes the resource data model. 45 | type SudoCmdGroupResourceModel struct { 46 | Id types.String `tfsdk:"id"` 47 | Name types.String `tfsdk:"name"` 48 | Description types.String `tfsdk:"description"` 49 | } 50 | 51 | func (r *SudoCmdGroupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { 52 | resp.TypeName = req.ProviderTypeName + "_sudo_cmdgroup" 53 | } 54 | 55 | func (r *SudoCmdGroupResource) ConfigValidators(ctx context.Context) []resource.ConfigValidator { 56 | return []resource.ConfigValidator{} 57 | } 58 | 59 | func (r *SudoCmdGroupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { 60 | resp.Schema = schema.Schema{ 61 | // This description is used by the documentation generator and the language server. 62 | MarkdownDescription: "FreeIPA Sudo command group resource", 63 | 64 | Attributes: map[string]schema.Attribute{ 65 | "id": schema.StringAttribute{ 66 | MarkdownDescription: "ID of the resource", 67 | Computed: true, 68 | PlanModifiers: []planmodifier.String{ 69 | stringplanmodifier.UseStateForUnknown(), 70 | }, 71 | }, 72 | "name": schema.StringAttribute{ 73 | MarkdownDescription: "Name of the sudo command group", 74 | Required: true, 75 | PlanModifiers: []planmodifier.String{ 76 | stringplanmodifier.RequiresReplace(), 77 | }, 78 | }, 79 | "description": schema.StringAttribute{ 80 | MarkdownDescription: "Sudo command group description", 81 | Optional: true, 82 | }, 83 | }, 84 | } 85 | } 86 | 87 | func (r *SudoCmdGroupResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { 88 | // Prevent panic if the provider has not been configured. 89 | if req.ProviderData == nil { 90 | return 91 | } 92 | 93 | client, ok := req.ProviderData.(*ipa.Client) 94 | 95 | if !ok { 96 | resp.Diagnostics.AddError( 97 | "Unexpected Resource Configure Type", 98 | fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), 99 | ) 100 | 101 | return 102 | } 103 | 104 | r.client = client 105 | } 106 | 107 | func (r *SudoCmdGroupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { 108 | var data SudoCmdGroupResourceModel 109 | 110 | // Read Terraform plan data into the model 111 | resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) 112 | 113 | if resp.Diagnostics.HasError() { 114 | return 115 | } 116 | 117 | optArgs := ipa.SudocmdgroupAddOptionalArgs{} 118 | 119 | args := ipa.SudocmdgroupAddArgs{ 120 | Cn: data.Name.ValueString(), 121 | } 122 | 123 | if !data.Description.IsNull() { 124 | optArgs.Description = data.Description.ValueStringPointer() 125 | } 126 | _, err := r.client.SudocmdgroupAdd(&args, &optArgs) 127 | if err != nil { 128 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error creating freeipa sudo command group: %s", err)) 129 | } 130 | data.Id = data.Name 131 | 132 | if resp.Diagnostics.HasError() { 133 | return 134 | } 135 | 136 | // Save data into Terraform state 137 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 138 | } 139 | 140 | func (r *SudoCmdGroupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { 141 | var data SudoCmdGroupResourceModel 142 | 143 | // Read Terraform prior state data into the model 144 | resp.Diagnostics.Append(req.State.Get(ctx, &data)...) 145 | 146 | if resp.Diagnostics.HasError() { 147 | return 148 | } 149 | 150 | all := true 151 | args := ipa.SudocmdgroupShowArgs{ 152 | Cn: data.Name.ValueString(), 153 | } 154 | optArgs := ipa.SudocmdgroupShowOptionalArgs{ 155 | All: &all, 156 | } 157 | 158 | res, err := r.client.SudocmdgroupShow(&args, &optArgs) 159 | if err != nil { 160 | if strings.Contains(err.Error(), "NotFound") { 161 | tflog.Debug(ctx, "[DEBUG] Sudo command group not found") 162 | resp.State.RemoveResource(ctx) 163 | return 164 | } else { 165 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error reading freeipa sudo command group: %s", err)) 166 | return 167 | } 168 | } 169 | if res != nil { 170 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Read freeipa sudo command group %s", res.Result.String())) 171 | } else { 172 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error reading freeipa sudo command group %s", data.Name.ValueString())) 173 | return 174 | } 175 | 176 | if res.Result.Description != nil && !data.Description.IsNull() { 177 | data.Description = types.StringValue(*res.Result.Description) 178 | } 179 | data.Id = data.Name 180 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Read freeipa sudo command group %s", res.Result.Cn)) 181 | 182 | // Save updated data into Terraform state 183 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 184 | if resp.Diagnostics.HasError() { 185 | return 186 | } 187 | } 188 | 189 | func (r *SudoCmdGroupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { 190 | var data, state SudoCmdGroupResourceModel 191 | 192 | // Read Terraform plan data into the model 193 | resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) 194 | resp.Diagnostics.Append(req.State.Get(ctx, &state)...) 195 | 196 | if resp.Diagnostics.HasError() { 197 | return 198 | } 199 | 200 | optArgs := ipa.SudocmdgroupModOptionalArgs{} 201 | 202 | args := ipa.SudocmdgroupModArgs{ 203 | Cn: data.Name.ValueString(), 204 | } 205 | 206 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Update freeipa sudo command group %s from plan = %v", data.Name.ValueString(), data)) 207 | if !data.Description.Equal(state.Description) { 208 | if data.Description.ValueStringPointer() != nil { 209 | optArgs.Description = data.Description.ValueStringPointer() 210 | } else { 211 | v := "" 212 | optArgs.Description = &v 213 | } 214 | } 215 | _, err := r.client.SudocmdgroupMod(&args, &optArgs) 216 | if err != nil { 217 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error updating freeipa sudo command group: %s", err)) 218 | } 219 | 220 | data.Id = data.Name 221 | 222 | if resp.Diagnostics.HasError() { 223 | return 224 | } 225 | 226 | // Save updated data into Terraform state 227 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 228 | } 229 | 230 | func (r *SudoCmdGroupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { 231 | var data SudoCmdGroupResourceModel 232 | 233 | // Read Terraform prior state data into the model 234 | resp.Diagnostics.Append(req.State.Get(ctx, &data)...) 235 | 236 | if resp.Diagnostics.HasError() { 237 | return 238 | } 239 | 240 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Delete freeipa sudo command group Id %s", data.Id.ValueString())) 241 | tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Delete freeipa sudo command group Name %s", data.Name.ValueString())) 242 | args := ipa.SudocmdgroupDelArgs{ 243 | Cn: []string{data.Name.ValueString()}, 244 | } 245 | optArgs := ipa.SudocmdgroupDelOptionalArgs{} 246 | _, err := r.client.SudocmdgroupDel(&args, &optArgs) 247 | if err != nil { 248 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("[DEBUG] Sudo command group %s deletion failed: %s", data.Id.ValueString(), err)) 249 | return 250 | } 251 | 252 | } 253 | 254 | func (r *SudoCmdGroupResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { 255 | resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) 256 | } 257 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/path" 22 | "github.com/hashicorp/terraform-plugin-framework/resource" 23 | "github.com/hashicorp/terraform-plugin-framework/resource/schema" 24 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" 25 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" 26 | "github.com/hashicorp/terraform-plugin-framework/types" 27 | "github.com/hashicorp/terraform-plugin-log/tflog" 28 | ipa "github.com/infra-monkey/go-freeipa/freeipa" 29 | "golang.org/x/exp/slices" 30 | ) 31 | 32 | // Ensure provider defined types fully satisfy framework interfaces. 33 | var _ resource.Resource = &SudoRuleOptionResource{} 34 | var _ resource.ResourceWithImportState = &SudoRuleOptionResource{} 35 | 36 | func NewSudoRuleOptionResource() resource.Resource { 37 | return &SudoRuleOptionResource{} 38 | } 39 | 40 | // SudoRuleOptionResource defines the resource implementation. 41 | type SudoRuleOptionResource struct { 42 | client *ipa.Client 43 | } 44 | 45 | // SudoRuleOptionResourceModel describes the resource data model. 46 | type SudoRuleOptionResourceModel struct { 47 | Id types.String `tfsdk:"id"` 48 | Name types.String `tfsdk:"name"` 49 | Option types.String `tfsdk:"option"` 50 | } 51 | 52 | func (r *SudoRuleOptionResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { 53 | resp.TypeName = req.ProviderTypeName + "_sudo_rule_option" 54 | } 55 | 56 | func (r *SudoRuleOptionResource) ConfigValidators(ctx context.Context) []resource.ConfigValidator { 57 | return []resource.ConfigValidator{} 58 | } 59 | 60 | func (r *SudoRuleOptionResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { 61 | resp.Schema = schema.Schema{ 62 | // This description is used by the documentation generator and the language server. 63 | MarkdownDescription: "FreeIPA Sudo rule option resource", 64 | 65 | Attributes: map[string]schema.Attribute{ 66 | "id": schema.StringAttribute{ 67 | MarkdownDescription: "ID of the resource", 68 | Computed: true, 69 | PlanModifiers: []planmodifier.String{ 70 | stringplanmodifier.UseStateForUnknown(), 71 | }, 72 | }, 73 | "name": schema.StringAttribute{ 74 | MarkdownDescription: "Sudo rule name", 75 | Required: true, 76 | PlanModifiers: []planmodifier.String{ 77 | stringplanmodifier.RequiresReplace(), 78 | }, 79 | }, 80 | "option": schema.StringAttribute{ 81 | MarkdownDescription: "Sudo option to add to the sudo rule.", 82 | Required: true, 83 | PlanModifiers: []planmodifier.String{ 84 | stringplanmodifier.RequiresReplace(), 85 | }, 86 | }, 87 | }, 88 | } 89 | } 90 | 91 | func (r *SudoRuleOptionResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { 92 | // Prevent panic if the provider has not been configured. 93 | if req.ProviderData == nil { 94 | return 95 | } 96 | 97 | client, ok := req.ProviderData.(*ipa.Client) 98 | 99 | if !ok { 100 | resp.Diagnostics.AddError( 101 | "Unexpected Resource Configure Type", 102 | fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), 103 | ) 104 | 105 | return 106 | } 107 | 108 | r.client = client 109 | } 110 | 111 | func (r *SudoRuleOptionResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { 112 | var data SudoRuleOptionResourceModel 113 | var id string 114 | 115 | // Read Terraform plan data into the model 116 | resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) 117 | 118 | if resp.Diagnostics.HasError() { 119 | return 120 | } 121 | 122 | optArgs := ipa.SudoruleAddOptionOptionalArgs{} 123 | 124 | args := ipa.SudoruleAddOptionArgs{ 125 | Cn: data.Name.ValueString(), 126 | Ipasudoopt: []string{data.Option.ValueString()}, 127 | } 128 | _, err := r.client.SudoruleAddOption(&args, &optArgs) 129 | if err != nil { 130 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error creating freeipa sudo rule option: %s", err)) 131 | return 132 | } 133 | 134 | id = fmt.Sprintf("%s/sro/%s", encodeSlash(data.Name.ValueString()), data.Option.ValueString()) 135 | data.Id = types.StringValue(id) 136 | 137 | // Save data into Terraform state 138 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 139 | } 140 | 141 | func (r *SudoRuleOptionResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { 142 | var data SudoRuleOptionResourceModel 143 | 144 | // Read Terraform prior state data into the model 145 | resp.Diagnostics.Append(req.State.Get(ctx, &data)...) 146 | 147 | if resp.Diagnostics.HasError() { 148 | return 149 | } 150 | 151 | sudoruleId, typeId, optId, err := parseSudoRuleOptionID(data.Id.ValueString()) 152 | 153 | if err != nil { 154 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error parsing ID of freeipa_sudorule_host_membership: %s", err)) 155 | return 156 | } 157 | 158 | all := true 159 | optArgs := ipa.SudoruleShowOptionalArgs{ 160 | All: &all, 161 | } 162 | 163 | args := ipa.SudoruleShowArgs{ 164 | Cn: sudoruleId, 165 | } 166 | 167 | res, err := r.client.SudoruleShow(&args, &optArgs) 168 | if err != nil { 169 | if strings.Contains(err.Error(), "NotFound") { 170 | tflog.Debug(ctx, "[DEBUG] Sudo rule not found") 171 | resp.State.RemoveResource(ctx) 172 | return 173 | } else { 174 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error reading freeipa sudo rule: %s", err)) 175 | return 176 | } 177 | } 178 | 179 | switch typeId { 180 | case "sro": 181 | if res.Result.Ipasudoopt == nil || !slices.Contains(*res.Result.Ipasudoopt, optId) { 182 | tflog.Debug(ctx, "[DEBUG] Sudo rule option does not exist") 183 | resp.State.RemoveResource(ctx) 184 | return 185 | } 186 | } 187 | 188 | // Save updated data into Terraform state 189 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 190 | if resp.Diagnostics.HasError() { 191 | return 192 | } 193 | } 194 | 195 | func (r *SudoRuleOptionResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { 196 | var data, state SudoRuleOptionResourceModel 197 | 198 | // Read Terraform plan data into the model 199 | resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) 200 | resp.Diagnostics.Append(req.State.Get(ctx, &state)...) 201 | 202 | if resp.Diagnostics.HasError() { 203 | return 204 | } 205 | 206 | // Save updated data into Terraform state 207 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 208 | } 209 | 210 | func (r *SudoRuleOptionResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { 211 | var data SudoRuleOptionResourceModel 212 | 213 | // Read Terraform prior state data into the model 214 | resp.Diagnostics.Append(req.State.Get(ctx, &data)...) 215 | 216 | if resp.Diagnostics.HasError() { 217 | return 218 | } 219 | cmdgrpId, typeId, optId, err := parseSudoRuleOptionID(data.Id.ValueString()) 220 | 221 | if err != nil { 222 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error parsing ID of freeipa_sudo_rule_option: %s", err)) 223 | return 224 | } 225 | 226 | optArgs := ipa.SudoruleRemoveOptionOptionalArgs{} 227 | 228 | args := ipa.SudoruleRemoveOptionArgs{ 229 | Cn: cmdgrpId, 230 | } 231 | 232 | switch typeId { 233 | case "sro": 234 | args.Ipasudoopt = []string{optId} 235 | } 236 | 237 | _, err = r.client.SudoruleRemoveOption(&args, &optArgs) 238 | if err != nil { 239 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error delete freeipa sudo host membership: %s", err)) 240 | return 241 | } 242 | } 243 | 244 | func (r *SudoRuleOptionResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { 245 | resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) 246 | } 247 | 248 | func parseSudoRuleOptionID(id string) (string, string, string, error) { 249 | idParts := strings.SplitN(id, "/", 3) 250 | if len(idParts) < 3 { 251 | return "", "", "", fmt.Errorf("unable to determine sudo rule option ID %s", id) 252 | } 253 | 254 | name := decodeSlash(idParts[0]) 255 | _type := idParts[1] 256 | opt := idParts[2] 257 | 258 | return name, _type, opt, nil 259 | } 260 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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.2 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.8.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.7 // 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.21.0 // 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.7 // 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /templates/data-sources.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "{{ .Name }} {{ .Type }} - {{ .ProviderShortName }}" 3 | description: |- 4 | {{.Description}} 5 | --- 6 | 7 | # {{ .Name }} ({{ .Type }}) 8 | 9 | 10 | {{if .HasExample}} 11 | ## Example Usage 12 | 13 | {{tffile .ExampleFile}} 14 | {{end}} 15 | 16 | {{ .SchemaMarkdown | trimspace }} 17 | -------------------------------------------------------------------------------- /templates/index.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "Provider: {{ .ProviderShortName | upper }}" 3 | description: |- 4 | 5 | --- 6 | 7 | # {{ .ProviderShortName | upper }} Provider 8 | 9 | ## Example Usage 10 | 11 | {{if .HasExample}}{{tffile .ExampleFile}}{{end}} 12 | 13 | {{ .SchemaMarkdown | trimspace }} 14 | -------------------------------------------------------------------------------- /templates/resources.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "{{ .Name }} {{ .Type }} - {{ .ProviderShortName }}" 3 | description: |- 4 | {{.Description}} 5 | --- 6 | 7 | # {{ .Name }} ({{ .Type }}) 8 | 9 | 10 | {{if .HasExample}} 11 | ## Example Usage 12 | 13 | {{tffile .ExampleFile}} 14 | {{end}} 15 | 16 | {{if .HasImport}} 17 | ## Import Usage 18 | 19 | {{tffile .ImportFile}} 20 | {{end}} 21 | 22 | {{ .SchemaMarkdown | trimspace }} 23 | -------------------------------------------------------------------------------- /terraform-registry-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "metadata": { 4 | "protocol_versions": ["6.0"] 5 | } 6 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------