├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── build.yml │ ├── release.yml │ ├── test.yml │ ├── tfplugindocs-check.yml │ └── tfplugindocs-generate.yml ├── .gitignore ├── .goreleaser.yml ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE ├── MAINTAINERS.md ├── Makefile ├── README.md ├── client ├── client.go ├── client_test.go ├── config.go ├── group.go ├── immutable_tag_rule.go ├── labels.go ├── misc.go ├── misc_test.go ├── preheat_instance.go ├── project.go ├── project_members.go ├── project_repositories.go ├── project_webhook.go ├── registry.go ├── replication.go ├── retention.go ├── robot_account.go ├── system.go ├── systemcve.go └── user.go ├── docs ├── data-sources │ ├── groups.md │ ├── project.md │ ├── project_member_groups.md │ ├── project_member_users.md │ ├── projects.md │ ├── registry.md │ ├── robot_accounts.md │ └── users.md ├── index.md └── resources │ ├── config_auth.md │ ├── config_security.md │ ├── config_system.md │ ├── garbage_collection.md │ ├── group.md │ ├── immutable_tag_rule.md │ ├── interrogation_services.md │ ├── label.md │ ├── preheat_instance.md │ ├── project.md │ ├── project_member_group.md │ ├── project_member_user.md │ ├── project_webhook.md │ ├── purge_audit_log.md │ ├── registry.md │ ├── replication.md │ ├── retention_policy.md │ ├── robot_account.md │ ├── tasks.md │ └── user.md ├── examples ├── data-sources │ ├── harbor_groups │ │ └── data-source.tf │ ├── harbor_project │ │ └── data-source.tf │ ├── harbor_project_member_groups │ │ └── data-source.tf │ ├── harbor_project_member_users │ │ └── data-source.tf │ ├── harbor_projects │ │ └── data-source.tf │ ├── harbor_registry │ │ └── data-source.tf │ ├── harbor_robot_accounts │ │ └── data-source.tf │ └── harbor_users │ │ └── data-source.tf ├── provider │ └── provider.tf └── resources │ ├── harbor_config_auth │ ├── ldap.tf │ └── oidc.tf │ ├── harbor_config_security │ └── resource.tf │ ├── harbor_config_system │ └── resource.tf │ ├── harbor_garbage_collection │ └── resource.tf │ ├── harbor_group │ └── resource.tf │ ├── harbor_immutable_tag_rule │ └── resource.tf │ ├── harbor_interrogation_services │ └── resource.tf │ ├── harbor_label │ ├── global.tf │ └── project.tf │ ├── harbor_preheat_instance │ ├── authentification.tf │ └── basic.tf │ ├── harbor_project │ ├── proxy.tf │ └── resource.tf │ ├── harbor_project_member_group │ └── resource.tf │ ├── harbor_project_member_user │ └── resource.tf │ ├── harbor_project_webhook │ └── resource.tf │ ├── harbor_purge_audit_log │ └── resource.tf │ ├── harbor_registry │ └── resource.tf │ ├── harbor_replication │ └── resource.tf │ ├── harbor_retention_policy │ └── resource.tf │ ├── harbor_robot_account │ ├── project.tf │ └── resource.tf │ ├── harbor_tasks │ └── resource.tf │ └── harbor_user │ └── resource.tf ├── go.mod ├── go.sum ├── kind-cluster.yaml ├── main.go ├── models ├── config.go ├── group.go ├── headers.go ├── immutable_tag_rule.go ├── interogations.go ├── labels.go ├── preheat_instance.go ├── project_members.go ├── project_webhooks.go ├── projects.go ├── quota.go ├── registry.go ├── replications.go ├── repositories.go ├── retention.go ├── robot_account.go ├── system.go ├── systemcve.go └── user.go ├── provider ├── data_groups.go ├── data_project.go ├── data_project_member_groups.go ├── data_project_member_users.go ├── data_projects.go ├── data_registry.go ├── data_registry_test.go ├── data_robot_accounts.go ├── data_users.go ├── provider.go ├── provider_test.go ├── random.go ├── random_test.go ├── resource_config_auth.go ├── resource_config_security.go ├── resource_config_system.go ├── resource_config_system_test.go ├── resource_garbage_collection.go ├── resource_garbage_collection_test.go ├── resource_group.go ├── resource_harbor_project_webhook.go ├── resource_harbor_project_webhook_test.go ├── resource_immutable_tag_rule.go ├── resource_interrogation_services.go ├── resource_labels.go ├── resource_labels_test.go ├── resource_log_rotation.go ├── resource_log_rotation_test.go ├── resource_preheat__instance.go ├── resource_preheat_instance_test.go ├── resource_project.go ├── resource_project_member_group.go ├── resource_project_member_group_test.go ├── resource_project_member_user.go ├── resource_project_member_user_test.go ├── resource_project_test.go ├── resource_registry.go ├── resource_registry_test.go ├── resource_replication.go ├── resource_replication_test.go ├── resource_retention_policy.go ├── resource_retention_policy_test.go ├── resource_robot_account.go ├── resource_robot_account_test.go ├── resource_tasks.go ├── resource_user.go └── resource_user_test.go ├── sonar-project.properties ├── templates ├── data-sources │ ├── groups.md.tmpl │ ├── project.md.tmpl │ ├── project_member_groups.md.tmpl │ ├── project_member_users.md.tmpl │ ├── projects.md.tmpl │ ├── registry.md.tmpl │ ├── robot_accounts.md.tmpl │ └── users.md.tmpl ├── index.md.tmpl └── resources │ ├── config_auth.md.tmpl │ ├── config_security.md.tmpl │ ├── config_system.md.tmpl │ ├── garbage_collection.md.tmpl │ ├── group.md.tmpl │ ├── immutable_tag_rule.md.tmpl │ ├── interrogation_services.md.tmpl │ ├── label.md.tmpl │ ├── preheat_instance.md.tmpl │ ├── project.md.tmpl │ ├── project_member_group.md.tmpl │ ├── project_member_user.md.tmpl │ ├── project_webhook.md.tmpl │ ├── purge_audit_log.md.tmpl │ ├── registry.md.tmpl │ ├── replication.md.tmpl │ ├── retention_policy.md.tmpl │ ├── robot_account.md.tmpl │ ├── tasks.md.tmpl │ └── user.md.tmpl ├── terraform-registry-manifest.json └── tools ├── go.mod ├── go.sum └── main.go /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Additional context** 20 | Add any other context about the problem here. 21 | 22 | - Provider Version [e.g. 22] 23 | - Terraform Version [e.g. 22] 24 | - Harbor Version [e.g. 22] 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # See GitHub's docs for more information on this file: 2 | # https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates 3 | version: 2 4 | updates: 5 | # Maintain dependencies for GitHub Actions 6 | - package-ecosystem: "github-actions" 7 | directory: "/" 8 | schedule: 9 | # Check for updates to GitHub Actions every weekday 10 | interval: "monthly" 11 | 12 | # Maintain dependencies for Go modules 13 | - package-ecosystem: "gomod" 14 | directory: "/" 15 | schedule: 16 | # Check for updates to Go modules every weekday 17 | interval: "monthly" -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | types: [opened, synchronize, reopened] 8 | jobs: 9 | sonarcloud: 10 | name: SonarCloud 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 16 | - name: SonarCloud Scan 17 | uses: SonarSource/sonarcloud-github-action@master 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any 20 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 21 | -------------------------------------------------------------------------------- /.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 | permissions: 18 | contents: write 19 | jobs: 20 | goreleaser: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - 24 | name: Checkout 25 | uses: actions/checkout@v4 26 | - 27 | name: Unshallow 28 | run: git fetch --prune --unshallow 29 | - 30 | name: Set up Go 31 | uses: actions/setup-go@v5.4.0 32 | with: 33 | go-version-file: 'go.mod' 34 | cache: true 35 | - 36 | name: Import GPG key 37 | uses: crazy-max/ghaction-import-gpg@v6 38 | id: import_gpg 39 | with: 40 | gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} 41 | passphrase: ${{ secrets.PASSPHRASE }} 42 | - 43 | name: Run GoReleaser 44 | uses: goreleaser/goreleaser-action@v6.3.0 45 | with: 46 | version: latest 47 | args: release --clean 48 | env: 49 | GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} 50 | # GitHub sets this automatically 51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/tfplugindocs-check.yml: -------------------------------------------------------------------------------- 1 | # source https://github.com/hashicorp/terraform-provider-awscc/blob/main/.github/workflows/tfplugindocs-check.yml 2 | 3 | name: Check if tfplugindocs result matches /docs 4 | 5 | on: 6 | push: 7 | branches: 8 | - main 9 | - "release/**" 10 | 11 | jobs: 12 | tfplugindocs_check: 13 | name: tfplugindocs check 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4.1.1 17 | - uses: actions/setup-go@v5.4.0 18 | with: 19 | go-version-file: tools/go.mod 20 | - name: GOCACHE 21 | run: | 22 | echo "GOCACHE=$(go env GOCACHE)" >> $GITHUB_ENV 23 | - uses: actions/cache@v4.2.3 24 | continue-on-error: true 25 | timeout-minutes: 2 26 | with: 27 | # TODO: Replace with supported mechanism when it is supported 28 | # https://github.com/actions/setup-go/issues/54 29 | path: ${{ env.GOCACHE }} 30 | key: ${{ runner.os }}-GOCACHE-${{ hashFiles('go.sum') }}-${{ hashFiles('internal/**') }} 31 | - uses: actions/cache@v4.2.3 32 | continue-on-error: true 33 | timeout-minutes: 2 34 | with: 35 | path: ~/go/pkg/mod 36 | key: ${{ runner.os }}-go-pkg-mod-${{ hashFiles('go.sum') }} 37 | - run: | 38 | make tools 39 | make docs 40 | git add -N docs/ 41 | git diff --exit-code 42 | -------------------------------------------------------------------------------- /.github/workflows/tfplugindocs-generate.yml: -------------------------------------------------------------------------------- 1 | # source https://github.com/hashicorp/terraform-provider-awscc/blob/main/.github/workflows/tfplugindocs-check.yml 2 | 3 | name: Generate tfplugindocs /docs 4 | 5 | on: 6 | pull_request: 7 | paths: 8 | - .github/workflows/tfplugindocs-check.yml 9 | - Makefile 10 | - docs/** 11 | - examples/** 12 | - templates/** 13 | 14 | jobs: 15 | tfplugindocs_generate: 16 | name: tfplugindocs generate 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4.1.1 20 | with: 21 | persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token 22 | fetch-depth: 0 # otherwise, you will failed to push refs to dest repo 23 | - uses: actions/setup-go@v5.4.0 24 | with: 25 | go-version-file: tools/go.mod 26 | - name: GOCACHE 27 | run: | 28 | echo "GOCACHE=$(go env GOCACHE)" >> $GITHUB_ENV 29 | - uses: actions/cache@v4.2.3 30 | continue-on-error: true 31 | timeout-minutes: 2 32 | with: 33 | # TODO: Replace with supported mechanism when it is supported 34 | # https://github.com/actions/setup-go/issues/54 35 | path: ${{ env.GOCACHE }} 36 | key: ${{ runner.os }}-GOCACHE-${{ hashFiles('go.sum') }}-${{ hashFiles('internal/**') }} 37 | - uses: actions/cache@v4.2.3 38 | continue-on-error: true 39 | timeout-minutes: 2 40 | with: 41 | path: ~/go/pkg/mod 42 | key: ${{ runner.os }}-go-pkg-mod-${{ hashFiles('go.sum') }} 43 | - run: | 44 | make tools 45 | make docs 46 | - uses: actions-js/push@v1.5 47 | name: Github commit and push 48 | with: 49 | github_token: ${{ secrets.GITHUB_TOKEN }} 50 | branch: ${{ github.head_ref }} 51 | message: "[bot][skip ci] docs: tfplugindocs generate" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | terraform* 2 | .terraform 3 | example 4 | *.log 5 | dist/* 6 | -------------------------------------------------------------------------------- /.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 Terraform Cloud where 12 | # they are unable to install libraries. 13 | - CGO_ENABLED=0 14 | mod_timestamp: '{{ .CommitTimestamp }}' 15 | flags: 16 | - -trimpath 17 | ldflags: 18 | - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' 19 | goos: 20 | - 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 | extra_files: 38 | - glob: 'terraform-registry-manifest.json' 39 | name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json' 40 | name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' 41 | algorithm: sha256 42 | signs: 43 | - artifacts: checksum 44 | args: 45 | # if you are using this in a GitHub action or some other automated pipeline, you 46 | # need to pass the batch flag to indicate its not interactive. 47 | - "--batch" 48 | - "--local-user" 49 | - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key 50 | - "--output" 51 | - "${signature}" 52 | - "--detach-sign" 53 | - "${artifact}" 54 | release: 55 | extra_files: 56 | - glob: 'terraform-registry-manifest.json' 57 | name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json' 58 | # If you want to manually examine the release before its live, uncomment this line: 59 | # draft: true 60 | changelog: 61 | disable: true -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # The last matching pattern takes precedence. 2 | # https://help.github.com/articles/about-codeowners/ 3 | 4 | * @goharbor/terraform-harbor-maintainers 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to terraform-provider-harbor 2 | 3 | ## Types of Contributions 4 | 5 | ### Report bugs 6 | Create a github [issue](https://github.com/goharbor/terraform-provider-harbor/issues) and label the issue with bug 7 | 8 | ### Fix bugs or create new resources 9 | Look through the Github issues for [bugs](https://github.com/goharbor/terraform-provider-harbor/labels/bugs), [new resource](https://github.com/goharbor/terraform-provider-harbor/labels/enhancement) or [help wanted](https://github.com/goharbor/terraform-provider-harbor/labels/help%20wanted) 10 | 11 | ### Suggest a new resource or data type 12 | Create a github [issue](https://github.com/goharbor/terraform-provider-harbor/issues) and label the issue with enhancement. 13 | 14 | ### Write Documentation 15 | Create / update documentation to help people understand how to use the data / resource types. 16 | 17 | ### Write terraform acceptance Tests 18 | Write terraform acceptance tests so resources can be tested against a test instance of harbor to automatically validate the creation, update and deletion of terraform resources. For more information refer to terraform acceptance tests [here](https://www.terraform.io/docs/extend/testing/acceptance-tests/index.html). 19 | 20 | ### Pull requests 21 | Create meaningful Pull Request titles and link issues as we use these to create release notes. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 BESTSELLER 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # Harbor Terraform Provider Maintainers 2 | 3 | [GOVERNANCE.md](https://github.com/goharbor/community/blob/master/GOVERNANCE.md) 4 | describes governance guidelines and maintainer responsibilities. 5 | 6 | ## Maintainers 7 | 8 | | Maintainer | GitHub ID | Affiliation | 9 | |-----------------|-----------|-------------------------------| 10 | | Florian Blampey | flbla | [SNCF](https://www.sncf.com/) | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ACCTEST_PARALLELISM ?= 20 2 | GO_VER ?= go 3 | 4 | default: tools 5 | 6 | .PHONY: tools docs default 7 | 8 | all: tools docs 9 | 10 | tools: 11 | cd tools && $(GO_VER) install github.com/golangci/golangci-lint/cmd/golangci-lint 12 | cd tools && $(GO_VER) install github.com/pavius/impi/cmd/impi 13 | cd tools && $(GO_VER) install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs 14 | 15 | docs: 16 | rm -f docs/data-sources/*.md 17 | rm -f docs/resources/*.md 18 | @tfplugindocs generate 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform Harbor Provider 2 | [![GitHub release](https://img.shields.io/github/release/goharbor/terraform-provider-harbor.svg)](https://github.com/goharbor/terraform-provider-harbor/releases/) 3 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fgoharbor%2Fterraform-provider-harbor.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fgoharbor%2Fterraform-provider-harbor?ref=badge_shield) 4 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=terraform-provider-harbor&metric=alert_status)](https://sonarcloud.io/dashboard?id=terraform-provider-harbor) 5 | ![GitHub All Releases](https://img.shields.io/github/downloads/goharbor/terraform-provider-harbor/total) 6 | ![GitHub Releases](https://img.shields.io/github/downloads/goharbor/terraform-provider-harbor/latest/total) 7 | 8 | Configure and manage Harbor with Terraform and [Pulumi](https://github.com/pulumiverse/pulumi-harbor). 9 | 10 | 11 | ## Usage 12 | [See the docs for usage information](./docs). 13 | 14 | ## Compatibility and Acceptance Tests 15 | 16 | Our acceptance tests are run against specific versions of Harbor and Terraform to ensure compatibility. 17 | For the latest version of the provider, we ran the tests against the following versions of Harbor and Terraform: 18 | ### Harbor Versions 19 | - `2.10` 20 | - `2.11` 21 | - `2.12` 22 | 23 | ### Terraform Versions 24 | - `1.8` 25 | - `1.9` 26 | - `1.10` 27 | 28 | Please note that while we strive to maintain compatibility with these versions, we recommend always using the latest versions of Harbor and Terraform for the best experience. 29 | 30 | ## Contributing 31 | Everyone is welcome to contribute to this repository. Feel free to raise [issues](https://github.com/goharbor/terraform-provider-harbor/issues) or to submit [Pull Requests.](https://github.com/goharbor/terraform-provider-harbor/pulls) 32 | 33 | See Contributing guide lines [here](./CONTRIBUTING.md) 34 | 35 | ## Note 36 | 37 | This Harbor Terrafrom provider was initially developed by BESTSELLER. 38 | 39 | 40 | ## License 41 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fgoharbor%2Fterraform-provider-harbor.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fgoharbor%2Fterraform-provider-harbor?ref=badge_large) 42 | -------------------------------------------------------------------------------- /client/client_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | func TestExtractCsrfHeaders(t *testing.T) { 10 | var testCases = []struct { 11 | headers http.Header 12 | expectError bool 13 | }{ 14 | { 15 | map[string][]string{ 16 | "X-Harbor-Csrf-Token": {"1234"}, 17 | "Set-Cookie": {"_gorilla_csrf=5678; expires=in-the-future"}, 18 | }, 19 | false, 20 | }, 21 | { 22 | map[string][]string{ 23 | "X-Harbor-Csrf-Token": {"1234"}, 24 | "Set-Cookie": {"_gorilla_csrf=5678; expires=in-the-future"}, 25 | "Irrelevant-Header": {"irrelevant"}, 26 | }, 27 | false, 28 | }, 29 | { 30 | map[string][]string{ 31 | "X-Harbor-Csrf-Token": {"1234"}, 32 | "Set-Cookie": { 33 | "abc=9012; irrelevant_cookie=123", 34 | "_gorilla_csrf=5678; expires=in-the-future", 35 | }, 36 | }, 37 | false, 38 | }, 39 | { 40 | map[string][]string{ 41 | "X-Harbor-Csrf-Token": {"1234", "wrong"}, 42 | "Set-Cookie": {"_gorilla_csrf=5678; expires=in-the-future"}, 43 | }, 44 | true, 45 | }, 46 | { 47 | map[string][]string{ 48 | "X-Harbor-Csrf-Token": {"wrong", "1234"}, 49 | "Set-Cookie": {"_gorilla_csrf=5678; expires=in-the-future"}, 50 | }, 51 | true, 52 | }, 53 | { 54 | map[string][]string{ 55 | "X-Harbor-Csrf-Token": {"1234"}, 56 | "Set-Cookie": {"_gorilla_csrf=5678"}, 57 | }, 58 | true, 59 | }, 60 | { 61 | map[string][]string{ 62 | "X-Harbor-Csrf-Token": {"1234"}, 63 | "Set-Cookie": {"_gorilla_csrf; expires=in-the-future"}, 64 | }, 65 | true, 66 | }, 67 | { 68 | map[string][]string{ 69 | "Set-Cookie": {"_gorilla_csrf=5678; expires=in-the-future"}, 70 | }, 71 | true, 72 | }, 73 | { 74 | map[string][]string{ 75 | "X-Harbor-Csrf-Token": {"1234"}, 76 | }, 77 | true, 78 | }, 79 | { 80 | map[string][]string{}, 81 | true, 82 | }, 83 | } 84 | 85 | expected := http.Header{ 86 | "X-Harbor-Csrf-Token": {"1234"}, 87 | "Cookie": {"_gorilla_csrf=5678"}, 88 | } 89 | 90 | for _, testCase := range testCases { 91 | testname := fmt.Sprintf("%v,Xerror=%t", testCase.headers, testCase.expectError) 92 | t.Run(testname, func(t *testing.T) { 93 | actual, err := extractCsrfHeaders(testCase.headers) 94 | if testCase.expectError { 95 | if err == nil { 96 | t.Fatal("expected error but got nil") 97 | } else { 98 | return // Success 99 | } 100 | } 101 | if err != nil { 102 | t.Fatalf("got unexpected error: %v", err) 103 | } 104 | for key, expected_value := range expected { 105 | actual_value := actual.Get(key) 106 | if expected_value[0] != actual_value { 107 | t.Fatalf("%s was %s; want %s", key, expected_value, actual_value) 108 | } 109 | } 110 | for actual_key := range actual { 111 | if expected.Get(actual_key) == "" { 112 | t.Fatalf("unexpected key %s", actual_key) 113 | } 114 | } 115 | }) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /client/group.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/goharbor/terraform-provider-harbor/models" 5 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 6 | ) 7 | 8 | // GroupBody return a json body 9 | func GroupBody(d *schema.ResourceData) models.GroupBody { 10 | return models.GroupBody{ 11 | Groupname: d.Get("group_name").(string), 12 | GroupType: d.Get("group_type").(int), 13 | LdapGroupDn: d.Get("ldap_group_dn").(string), 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/immutable_tag_rule.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "log" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/goharbor/terraform-provider-harbor/models" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | ) 11 | 12 | func GetImmutableTagRuleBody(d *schema.ResourceData) *models.ImmutableTagRule { 13 | tagId := 0 14 | if d.Id() != "" { 15 | lastSlashIndex := strings.LastIndex(d.Id(), "/") 16 | tagId, _ = strconv.Atoi(d.Id()[lastSlashIndex+1:]) 17 | } 18 | 19 | tags := []models.ImmutableTagRuleTagSelectors{} 20 | tag := models.ImmutableTagRuleTagSelectors{ 21 | Kind: "doublestar", 22 | } 23 | 24 | if d.Get("tag_matching").(string) != "" { 25 | tag.Decoration = "matches" 26 | tag.Pattern = d.Get("tag_matching").(string) 27 | d.Set("tag_excluding", nil) 28 | } 29 | if d.Get("tag_excluding").(string) != "" { 30 | tag.Decoration = "excludes" 31 | tag.Pattern = d.Get("tag_excluding").(string) 32 | d.Set("tag_matching", nil) 33 | } 34 | tags = append(tags, tag) 35 | 36 | scopeSelectorsRepository := models.ScopeSelectors{ 37 | Repository: []models.Repository{}, 38 | } 39 | 40 | repo := models.Repository{ 41 | Kind: "doublestar", 42 | } 43 | 44 | if d.Get("repo_matching").(string) != "" { 45 | repo.Decoration = "repoMatches" 46 | repo.Pattern = d.Get("repo_matching").(string) 47 | d.Set("repo_excluding", nil) 48 | } 49 | if d.Get("repo_excluding").(string) != "" { 50 | repo.Decoration = "repoExcludes" 51 | repo.Pattern = d.Get("repo_excluding").(string) 52 | d.Set("repo_matching", nil) 53 | } 54 | scopeSelectorsRepository.Repository = append(scopeSelectorsRepository.Repository, repo) 55 | 56 | body := models.ImmutableTagRule{ 57 | Id: tagId, 58 | Disabled: d.Get("disabled").(bool), 59 | ScopeSelectors: scopeSelectorsRepository, 60 | ImmutableTagRuleTagSelectors: tags, 61 | Action: "immutable", 62 | Template: "immutable_template", 63 | } 64 | 65 | log.Printf("[DEBUG] %+v\n ", body) 66 | 67 | return &body 68 | } 69 | -------------------------------------------------------------------------------- /client/labels.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "log" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/goharbor/terraform-provider-harbor/models" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | ) 11 | 12 | func LabelsBody(d *schema.ResourceData) models.Labels { 13 | body := models.Labels{ 14 | Name: d.Get("name").(string), 15 | Description: d.Get("description").(string), 16 | Color: d.Get("color").(string), 17 | } 18 | 19 | project := d.Get("project_id").(string) 20 | if project != "" { 21 | id, err := strconv.Atoi(strings.ReplaceAll(project, "/projects/", "")) 22 | if err != nil { 23 | log.Println(err) 24 | } 25 | 26 | body.ProjectID = id 27 | body.Scope = "p" 28 | } else { 29 | 30 | body.Scope = "g" 31 | } 32 | return body 33 | } 34 | -------------------------------------------------------------------------------- /client/misc.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "regexp" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | func GetSchedule(schedule string) (typefmt string, cronfmt string) { 10 | var TypeStr string 11 | var CronStr string 12 | 13 | switch strings.ToLower(schedule) { 14 | case "hourly", "0 0 * * * *": 15 | TypeStr = "Hourly" 16 | CronStr = "0 0 * * * *" 17 | case "daily", "0 0 0 * * *": 18 | TypeStr = "Daily" 19 | CronStr = "0 0 0 * * *" 20 | case "weekly", "0 0 0 * * 0": 21 | TypeStr = "Weekly" 22 | CronStr = "0 0 0 * * 0" 23 | case "": 24 | TypeStr = "None" 25 | CronStr = "" 26 | default: 27 | TypeStr = "Custom" 28 | CronStr = schedule 29 | } 30 | 31 | return TypeStr, CronStr 32 | } 33 | 34 | var regexCron = regexp.MustCompile(`(?m)((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})`) 35 | 36 | func ParseBoolOrDefault(value string, defaultValue bool) (bool, error) { 37 | if value == "" { 38 | return defaultValue, nil 39 | } 40 | return strconv.ParseBool(value) 41 | } 42 | -------------------------------------------------------------------------------- /client/misc_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | ) 7 | 8 | func TestGetSchedule(t *testing.T) { 9 | 10 | schedule, cron := GetSchedule("hourly") 11 | 12 | re := regexp.MustCompile(`([A-Z][^\s]*)`) 13 | matched := re.MatchString(schedule) 14 | if matched == false { 15 | t.Error("Didn't return a Titled string") 16 | } 17 | 18 | matchCronStr := regexCron.MatchString(cron) 19 | if matchCronStr == false { 20 | t.Error("Invalid cron string") 21 | } 22 | } 23 | 24 | func TestParseBoolOrDefault(t *testing.T) { 25 | tests := []struct { 26 | name string 27 | value string 28 | defaultVal bool 29 | expectedVal bool 30 | expectError bool 31 | }{ 32 | {"Empty string, default false", "", false, false, false}, 33 | {"Empty string, default true", "", true, true, false}, 34 | {"Value 'true', default false", "true", false, true, false}, 35 | {"Value 'false', default true", "false", true, false, false}, 36 | {"Invalid value", "invalid", false, false, true}, 37 | } 38 | 39 | for _, tt := range tests { 40 | t.Run(tt.name, func(t *testing.T) { 41 | result, err := ParseBoolOrDefault(tt.value, tt.defaultVal) 42 | if (err != nil) != tt.expectError { 43 | t.Errorf("ParseBoolOrDefault() error = %v, expectError %v", err, tt.expectError) 44 | return 45 | } 46 | if result != tt.expectedVal { 47 | t.Errorf("ParseBoolOrDefault() = %v, want %v", result, tt.expectedVal) 48 | } 49 | }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /client/preheat_instance.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/goharbor/terraform-provider-harbor/models" 5 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 6 | ) 7 | 8 | func PreheatInstanceBody(d *schema.ResourceData) models.PreheatInstance { 9 | authInfo := models.PreheatInstanceAuthInfo{} 10 | authMode := d.Get("auth_mode").(string) 11 | 12 | switch authMode { 13 | case "NONE": 14 | // No token, username, or password 15 | case "BASIC": 16 | authInfo.Username = d.Get("username").(string) 17 | authInfo.Password = d.Get("password").(string) 18 | case "OAUTH": 19 | authInfo.Token = d.Get("token").(string) 20 | } 21 | 22 | body := models.PreheatInstance{ 23 | Name: d.Get("name").(string), 24 | Description: d.Get("description").(string), 25 | Vendor: d.Get("vendor").(string), 26 | Endpoint: d.Get("endpoint").(string), 27 | AuthMode: authMode, 28 | AuthInfo: authInfo, 29 | Enabled: d.Get("enabled").(bool), 30 | Default: d.Get("default").(bool), 31 | Insecure: d.Get("insecure").(bool), 32 | } 33 | return body 34 | } 35 | -------------------------------------------------------------------------------- /client/project_members.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/goharbor/terraform-provider-harbor/models" 5 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 6 | ) 7 | 8 | func ProjectMembersGroupBody(d *schema.ResourceData) models.ProjectMembersBodyPost { 9 | body := models.ProjectMembersBodyPost{ 10 | RoleID: RoleType(d.Get("role").(string)), 11 | GroupMember: models.ProjectMembersBodyGroup{ 12 | GroupType: GroupType(d.Get("type").(string)), 13 | GroupName: d.Get("group_name").(string), 14 | GroupID: d.Get("group_id").(int), 15 | }, 16 | } 17 | 18 | if v, ok := d.GetOk("ldap_group_dn"); ok { 19 | body.GroupMember.LdapGroupDN = v.(string) 20 | } 21 | return body 22 | } 23 | 24 | func ProjectMembersUserBody(d *schema.ResourceData) models.ProjectMembersBodyPost { 25 | return models.ProjectMembersBodyPost{ 26 | RoleID: RoleType(d.Get("role").(string)), 27 | UserMembers: models.ProjectMemberUsersGroup{ 28 | UserName: d.Get("user_name").(string), 29 | }, 30 | } 31 | } 32 | 33 | func GroupType(group string) (x int) { 34 | switch group { 35 | case "ldap": 36 | x = 1 37 | case "internal": 38 | x = 2 39 | case "oidc": 40 | x = 3 41 | } 42 | return x 43 | } 44 | 45 | func RoleTypeNumber(role int) (x string) { 46 | switch role { 47 | case 1: 48 | x = "projectadmin" 49 | case 2: 50 | x = "developer" 51 | case 3: 52 | x = "guest" 53 | case 4: 54 | x = "maintainer" 55 | case 5: 56 | x = "limitedguest" 57 | } 58 | return x 59 | } 60 | 61 | func RoleType(role string) (x int) { 62 | switch role { 63 | case "projectadmin": 64 | x = 1 65 | case "developer": 66 | x = 2 67 | case "guest": 68 | x = 3 69 | case "maintainer": 70 | x = 4 71 | case "limitedguest": 72 | x = 5 73 | } 74 | return x 75 | } 76 | -------------------------------------------------------------------------------- /client/project_repositories.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/goharbor/terraform-provider-harbor/models" 9 | ) 10 | 11 | // GetProjectRepositories returns a list of repositories for the given project. 12 | func (c *Client) GetProjectRepositories(projectName string) ([]models.RepositoryBody, error) { 13 | var allRepos []models.RepositoryBody 14 | 15 | page := 1 16 | 17 | // Page through the repository list until the API returns an empty result 18 | // or an error. 19 | for { 20 | reposPath := fmt.Sprintf("/projects/%s/repositories?page=%d&page_size=100", projectName, page) 21 | 22 | resp, _, _, err := c.SendRequest("GET", reposPath, nil, 200) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | var repos []models.RepositoryBody 28 | 29 | if err := json.Unmarshal([]byte(resp), &repos); err != nil { 30 | return nil, err 31 | } 32 | 33 | if len(repos) == 0 { 34 | return allRepos, nil 35 | } 36 | 37 | allRepos = append(allRepos, repos...) 38 | page++ 39 | } 40 | } 41 | 42 | // DeleteProjectRepositories deletes all repositories of a given project. 43 | func (c *Client) DeleteProjectRepositories(projectName string) error { 44 | repos, err := c.GetProjectRepositories(projectName) 45 | if err != nil { 46 | return err 47 | } 48 | 49 | // Repository names returned by the API have the form 50 | // /. 51 | projectNamePrefix := fmt.Sprintf("%s/", projectName) 52 | 53 | for _, repo := range repos { 54 | repoName := strings.TrimPrefix(repo.Name, projectNamePrefix) 55 | 56 | // Encode slashes in the repository name as mandated by the API. 57 | repoName = strings.ReplaceAll(repoName, "/", "%252F") 58 | 59 | repoPath := fmt.Sprintf("/projects/%s/repositories/%s", projectName, repoName) 60 | 61 | _, _, _, err := c.SendRequest("DELETE", repoPath, nil, 200) 62 | if err != nil { 63 | return err 64 | } 65 | } 66 | 67 | return nil 68 | } 69 | -------------------------------------------------------------------------------- /client/project_webhook.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/goharbor/terraform-provider-harbor/models" 5 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 6 | ) 7 | 8 | func ProjectWebhookBody(d *schema.ResourceData) models.ProjectWebhook { 9 | eventTypes := d.Get("events_types").([]interface{}) 10 | 11 | body := models.ProjectWebhook{ 12 | Name: d.Get("name").(string), 13 | Description: d.Get("description").(string), 14 | Enabled: d.Get("enabled").(bool), 15 | EventTypes: eventTypes, 16 | } 17 | targets := models.WebHookTargets{ 18 | Type: d.Get("notify_type").(string), 19 | AuthHeader: d.Get("auth_header").(string), 20 | SkipCertVerify: d.Get("skip_cert_verify").(bool), 21 | Address: d.Get("address").(string), 22 | } 23 | 24 | body.Targets = append(body.Targets, targets) 25 | 26 | return body 27 | } 28 | -------------------------------------------------------------------------------- /client/registry.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/goharbor/terraform-provider-harbor/models" 5 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 6 | ) 7 | 8 | func GetRegistryBody(d *schema.ResourceData) models.RegistryBody { 9 | regType, _ := GetRegistryType(d.Get("provider_name").(string)) 10 | 11 | body := models.RegistryBody{ 12 | Description: d.Get("description").(string), 13 | Insecure: d.Get("insecure").(bool), 14 | Name: d.Get("name").(string), 15 | Type: regType, 16 | URL: d.Get("endpoint_url").(string), 17 | } 18 | 19 | body.Credential.AccessKey = d.Get("access_id").(string) 20 | body.Credential.AccessSecret = d.Get("access_secret").(string) 21 | body.Credential.Type = "basic" 22 | 23 | return body 24 | } 25 | 26 | func GetRegistryUpdateBody(d *schema.ResourceData) models.RegistryUpdateBody { 27 | 28 | body := models.RegistryUpdateBody{ 29 | AccessKey: d.Get("access_id").(string), 30 | AccessSecret: d.Get("access_secret").(string), 31 | Description: d.Get("description").(string), 32 | Insecure: d.Get("insecure").(bool), 33 | Name: d.Get("name").(string), 34 | URL: d.Get("endpoint_url").(string), 35 | } 36 | 37 | return body 38 | } 39 | 40 | func GetRegistryType(regType string) (regName string, err error) { 41 | 42 | registryType := map[string]string{ 43 | "alibaba": "ali-acr", 44 | "artifact-hub": "artifact-hub", 45 | "aws": "aws-ecr", 46 | "azure": "azure-acr", 47 | "docker-hub": "docker-hub", 48 | "docker-registry": "docker-registry", 49 | "gitlab": "gitlab", 50 | "github": "github-ghcr", 51 | "google": "google-gcr", 52 | "harbor": "harbor", 53 | "helm": "helm-hub", 54 | "huawei": "huawei-SWR", 55 | "jfrog": "jfrog-artifactory", 56 | "quay": "quay", 57 | // for reverse lookup 58 | "ali-acr": "alibaba", 59 | "aws-ecr": "aws", 60 | "azure-acr": "azure", 61 | "github-ghcr": "github", 62 | "google-gcr": "google", 63 | "helm-hub": "helm", 64 | "huawei-SWR": "huawei", 65 | "jfrog-artifactory": "jfrog", 66 | "quay-io": "quay", 67 | } 68 | 69 | return registryType[regType], nil 70 | } 71 | -------------------------------------------------------------------------------- /client/replication.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "strings" 7 | 8 | "github.com/goharbor/terraform-provider-harbor/models" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | ) 11 | 12 | func GetReplicationBody(d *schema.ResourceData) models.ReplicationBody { 13 | 14 | action := d.Get("action").(string) 15 | schedule := d.Get("schedule").(string) 16 | 17 | body := models.ReplicationBody{ 18 | Name: d.Get("name").(string), 19 | Description: d.Get("description").(string), 20 | Override: d.Get("override").(bool), 21 | Enabled: d.Get("enabled").(bool), 22 | Deletion: d.Get("deletion").(bool), 23 | DestNamespace: d.Get("dest_namespace").(string), 24 | DestNamespaceReplace: d.Get("dest_namespace_replace").(int), 25 | CopyByChunk: d.Get("copy_by_chunk").(bool), 26 | Speed: d.Get("speed").(int), 27 | } 28 | 29 | if action == "push" { 30 | body.DestRegistry.ID = d.Get("registry_id").(int) 31 | } else if action == "pull" { 32 | body.SrcRegistry.ID = d.Get("registry_id").(int) 33 | } 34 | 35 | switch schedule { 36 | case "manual", "event_based": 37 | body.Trigger.Type = schedule 38 | break 39 | default: 40 | body.Trigger.Type = "scheduled" 41 | body.Trigger.TriggerSettings.Cron = schedule 42 | } 43 | 44 | filters := d.Get("filters").(*schema.Set).List() 45 | 46 | for _, data := range filters { 47 | data := data.(map[string]interface{}) 48 | filter := models.ReplicationFilters{} 49 | 50 | name := data["name"].(string) 51 | tag := data["tag"].(string) 52 | label := data["labels"].([]interface{}) 53 | decoration := data["decoration"].(string) 54 | resource := data["resource"].(string) 55 | 56 | if name != "" { 57 | filter.Type = "name" 58 | filter.Value = name 59 | } 60 | if tag != "" { 61 | filter.Type = "tag" 62 | filter.Value = strings.ReplaceAll(tag, " ", "") 63 | filter.Decoration = decoration 64 | } 65 | if len(label) > 0 { 66 | filter.Type = "label" 67 | filter.Value = make([]string, 0) 68 | for _, v := range label { 69 | filter.Value = append(filter.Value.([]string), fmt.Sprintf("%v", v)) 70 | } 71 | filter.Decoration = decoration 72 | } 73 | if resource != "" { 74 | filter.Type = "resource" 75 | filter.Value = resource 76 | } 77 | body.Filters = append(body.Filters, filter) 78 | 79 | } 80 | 81 | log.Println(body) 82 | return body 83 | } 84 | 85 | func GetExecutionBody(d *schema.ResourceData) models.ExecutionBody { 86 | 87 | body := models.ExecutionBody{ 88 | PolicyID: d.Get("policy_id").(int), 89 | } 90 | 91 | log.Println(body) 92 | return body 93 | } 94 | -------------------------------------------------------------------------------- /client/robot_account.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/goharbor/terraform-provider-harbor/models" 5 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 6 | ) 7 | 8 | func RobotBody(d *schema.ResourceData) models.RobotBody { 9 | body := models.RobotBody{ 10 | Name: d.Get("name").(string), 11 | Description: d.Get("description").(string), 12 | Disable: d.Get("disable").(bool), 13 | Duration: d.Get("duration").(int), 14 | Level: d.Get("level").(string), 15 | } 16 | 17 | permissions := d.Get("permissions").(*schema.Set).List() 18 | for _, p := range permissions { 19 | 20 | permission := models.RobotBodyPermission{ 21 | Kind: p.(map[string]interface{})["kind"].(string), 22 | Namespace: p.(map[string]interface{})["namespace"].(string), 23 | } 24 | 25 | for _, a := range p.(map[string]interface{})["access"].(*schema.Set).List() { 26 | access := models.RobotBodyAccess{ 27 | Action: a.(map[string]interface{})["action"].(string), 28 | Resource: a.(map[string]interface{})["resource"].(string), 29 | } 30 | if a.(map[string]interface{})["effect"] != "" { 31 | access.Effect = a.(map[string]interface{})["effect"].(string) 32 | } 33 | 34 | permission.Access = append(permission.Access, access) 35 | } 36 | 37 | body.Permissions = append(body.Permissions, permission) 38 | } 39 | 40 | return body 41 | } 42 | -------------------------------------------------------------------------------- /client/system.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "strings" 7 | 8 | "github.com/goharbor/terraform-provider-harbor/models" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | ) 11 | 12 | func GetSystemBoby(d *schema.ResourceData, scheduleType string) models.SystemBody { 13 | var schedule string 14 | 15 | if scheduleType == "gc" { 16 | schedule = d.Get("schedule").(string) 17 | } else if scheduleType == "vuln" { 18 | schedule = d.Get("vulnerability_scan_policy").(string) 19 | } else if scheduleType == "purgeaudit" { 20 | schedule = d.Get("schedule").(string) 21 | } 22 | 23 | TypeStr, CronStr := GetSchedule(schedule) 24 | 25 | body := models.SystemBody{} 26 | body.Schedule.Type = TypeStr 27 | body.Schedule.Cron = CronStr 28 | if scheduleType == "gc" { 29 | body.Parameters.DeleteUntagged = d.Get("delete_untagged").(bool) 30 | body.Parameters.Workers = d.Get("workers").(int) 31 | } else if scheduleType == "purgeaudit" { 32 | body.Parameters.AuditRetentionHour = d.Get("audit_retention_hour").(int) 33 | body.Parameters.IncludeOperations = d.Get("include_operations").(string) 34 | } 35 | 36 | return body 37 | } 38 | 39 | // SetSchedule sets the schedule time to perform Vuln scanning and GC 40 | func (client *Client) SetSchedule(d *schema.ResourceData, scheduleType string) (err error) { 41 | var path string 42 | 43 | if scheduleType == "gc" { 44 | path = models.PathGC 45 | } else if scheduleType == "vuln" { 46 | path = models.PathVuln 47 | } else if scheduleType == "purgeaudit" { 48 | path = models.PathPurgeAudit 49 | } 50 | 51 | body := GetSystemBoby(d, scheduleType) 52 | 53 | resp, _, _, err := client.SendRequest("GET", path, nil, 200) 54 | if err != nil { 55 | return err 56 | } 57 | 58 | requestType := "POST" 59 | httpStatusCode := 201 60 | 61 | if resp != "" { 62 | log.Printf("Schedule found performing PUT request") 63 | requestType = "PUT" 64 | httpStatusCode = 200 65 | } else { 66 | log.Printf("No Schedule found performing POST request") 67 | } 68 | 69 | _, _, _, err = client.SendRequest(requestType, path, body, httpStatusCode) 70 | if err != nil { 71 | return err 72 | 73 | } 74 | return nil 75 | } 76 | 77 | // SetDefaultScanner set the default scanner within harbor 78 | func (client *Client) SetDefaultScanner(scanner string) (err error) { 79 | resp, _, _, err := client.SendRequest("GET", models.PathScanners, nil, 0) 80 | 81 | body := models.ScannerBody{ 82 | IsDefault: true, 83 | } 84 | 85 | var jsonData []models.ScannerBody 86 | err = json.Unmarshal([]byte(resp), &jsonData) 87 | if err != nil { 88 | return err 89 | } 90 | 91 | for _, v := range jsonData { 92 | 93 | if v.Name == strings.Title(scanner) { 94 | _, _, _, err = client.SendRequest("PATCH", models.PathScanners+"/"+v.UUID, body, 0) 95 | } 96 | if err != nil { 97 | return err 98 | } 99 | } 100 | 101 | return nil 102 | } 103 | -------------------------------------------------------------------------------- /client/systemcve.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/goharbor/terraform-provider-harbor/models" 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 8 | ) 9 | 10 | func SystemCVEAllowListBody(d *schema.ResourceData) models.SystemCveAllowListBodyPost { 11 | body := models.SystemCveAllowListBodyPost{} 12 | 13 | expires_at, expires_at_true := d.GetOk("expires_at") 14 | 15 | if expires_at_true { 16 | body.ExpiresAt = expires_at.(int) 17 | } 18 | 19 | cveAllowList := d.Get("cve_allowlist").([]interface{}) 20 | log.Printf("[DEBUG] %v ", cveAllowList) 21 | if len(cveAllowList) > 0 { 22 | log.Printf("[DEBUG] %v ", expandSystemCveAllowList(cveAllowList)) 23 | body.Items = expandSystemCveAllowList(cveAllowList) 24 | } 25 | 26 | return body 27 | } 28 | 29 | func expandSystemCveAllowList(cveAllowlist []interface{}) models.SystemCveAllowlistItems { 30 | allowlist := models.SystemCveAllowlistItems{} 31 | 32 | for _, data := range cveAllowlist { 33 | item := models.SystemCveAllowlistItem{ 34 | CveID: data.(string), 35 | } 36 | allowlist = append(allowlist, item) 37 | } 38 | 39 | return allowlist 40 | } 41 | -------------------------------------------------------------------------------- /client/user.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/goharbor/terraform-provider-harbor/models" 5 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 6 | ) 7 | 8 | // UserBody return a json body 9 | func UserBody(d *schema.ResourceData) models.UserBody { 10 | return models.UserBody{ 11 | Username: d.Get("username").(string), 12 | Password: d.Get("password").(string), 13 | SysadminFlag: d.Get("admin").(bool), 14 | Email: d.Get("email").(string), 15 | Realname: d.Get("full_name").(string), 16 | Newpassword: d.Get("password").(string), 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /docs/data-sources/groups.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_groups Data Source - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_groups (Data Source) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | data "harbor_groups" "example" { 17 | group_name = "example-group" 18 | } 19 | 20 | output "group_ids" { 21 | value = [data.harbor_groups.example.*.id] 22 | } 23 | ``` 24 | 25 | ## Schema 26 | 27 | ### Optional 28 | 29 | - `group_name` (String) The name of the group to filter by. 30 | - `ldap_group_dn` (String) The LDAP group DN to filter by. 31 | 32 | ### Read-Only 33 | 34 | - `groups` (List of Object) (see [below for nested schema](#nestedatt--groups)) 35 | - `id` (String) The ID of this resource. 36 | 37 | 38 | 39 | ### Nested Schema for `groups` 40 | 41 | A list of groups matching the previous arguments. Each `group` object provides the attributes documented below. 42 | 43 | Read-Only: 44 | 45 | - `group_name` (String) The name of the group. 46 | - `group_type` (Number) The type of the group. 47 | - `id` (Number) The ID of the group. 48 | - `ldap_group_dn` (String) The LDAP group DN of the group. 49 | 50 | This data source retrieves a list of Harbor groups and filters them based on the `group_name` and `ldap_group_dn` arguments. It returns a list of `group` objects, each containing the `id`, `group_name`, `group_type`, and `ldap_group_dn` attributes of a group that matches the filter criteria. 51 | -------------------------------------------------------------------------------- /docs/data-sources/project.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_project Data Source - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_project (Data Source) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | data "harbor_project" "main" { 17 | name = "library" 18 | } 19 | 20 | output "project_id" { 21 | value = data.harbor_project.main.id 22 | } 23 | ``` 24 | 25 | ## Schema 26 | 27 | ### Required 28 | 29 | - `name` (String) The name of the project. 30 | 31 | ### Read-Only 32 | 33 | - `id` (String) The ID of this resource. 34 | - `project_id` (Number) The id of the project within harbor. 35 | - `public` (Boolean) If the project has public accessibility. 36 | - `type` (String) The type of the project : Project or ProxyCache. 37 | - `vulnerability_scanning` (Boolean) If the images is scanned for vulnerabilities when push to harbor. 38 | -------------------------------------------------------------------------------- /docs/data-sources/project_member_groups.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_project_member_groups Data Source - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_project_member_groups (Data Source) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | data "harbor_project_member_groups" "example" { 17 | project_id = "1" 18 | } 19 | 20 | output "project_member_group_ids" { 21 | value = [data.harbor_project_member_groups.example.project_member_groups.*.id] 22 | } 23 | ``` 24 | 25 | ## Schema 26 | 27 | ### Required 28 | 29 | - `project_id` (String) The id of the project within harbor. 30 | 31 | ### Read-Only 32 | 33 | - `id` (String) The ID of this resource. 34 | - `project_member_groups` (List of Object) (see [below for nested schema](#nestedatt--project_member_groups)) 35 | 36 | 37 | 38 | ### Nested Schema for `project_member_groups` 39 | 40 | A list of project member group matching the previous arguments. Each `project_member_group` object provides the attributes documented below. 41 | 42 | Read-Only: 43 | 44 | - `id` (Number) The ID of this resource. 45 | - `project_id` (String) The id of the project that the group has access to. 46 | - `group_name` (String) The name of the group. 47 | - `role` (String) The permissions that the group is granted. 48 | 49 | This data source retrieves a list of Harbor project member groups and filters them based on the `project_id` argument. It returns a list of `project_member_group` objects, each containing the `id`, `project_id`, `group_name` and `role` attributes of a project member group that matches the filter criteria. 50 | -------------------------------------------------------------------------------- /docs/data-sources/project_member_users.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_project_member_users Data Source - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_project_member_users (Data Source) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | data "harbor_project_member_users" "example" { 17 | project_id = "1" 18 | } 19 | 20 | output "project_member_user_ids" { 21 | value = [data.harbor_project_member_users.example.project_member_users.*.id] 22 | } 23 | ``` 24 | 25 | ## Schema 26 | 27 | ### Required 28 | 29 | - `project_id` (String) The id of the project within harbor. 30 | 31 | ### Read-Only 32 | 33 | - `id` (String) The ID of this resource. 34 | - `project_member_users` (List of Object) (see [below for nested schema](#nestedatt--project_member_users)) 35 | 36 | 37 | 38 | ### Nested Schema for `project_member_users` 39 | 40 | A list of project member user matching the previous arguments. Each `project_member_user` object provides the attributes documented below. 41 | 42 | Read-Only: 43 | 44 | - `id` (Number) The ID of this resource. 45 | - `project_id` (String) The id of the project that the user has access to. 46 | - `user_name` (String) The name of the user. 47 | - `role` (String) The permissions that the user is granted. 48 | 49 | This data source retrieves a list of Harbor project member users and filters them based on the `project_id` argument. It returns a list of `project_member_user` objects, each containing the `id`, `project_id`, `user_name` and `role` attributes of a project member user that matches the filter criteria. 50 | -------------------------------------------------------------------------------- /docs/data-sources/projects.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_projects Data Source - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_projects (Data Source) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | data "harbor_projects" "proxycache" { 17 | type = "ProxyCache" 18 | } 19 | 20 | output "proxy_cache_projects" { 21 | value = data.harbor_projects.proxycache 22 | } 23 | ``` 24 | 25 | ## Schema 26 | 27 | ### Optional 28 | 29 | - `name` (String) The name of the project. 30 | - `public` (Boolean) If the project has public accessibility. 31 | - `type` (String) The type of the project : Project or ProxyCache. 32 | - `vulnerability_scanning` (Boolean) If the images will be scanned for vulnerabilities when push to harbor. 33 | 34 | ### Read-Only 35 | 36 | - `id` (String) The ID of this resource. 37 | - `projects` (List of Object) (see [below for nested schema](#nestedatt--projects)) 38 | 39 | 40 | 41 | ### Nested Schema for `projects` 42 | 43 | Read-Only: 44 | 45 | - `name` (String) 46 | - `project_id` (Number) 47 | - `public` (Boolean) 48 | - `type` (String) 49 | - `vulnerability_scanning` (Boolean) 50 | -------------------------------------------------------------------------------- /docs/data-sources/registry.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_registry Data Source - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_registry (Data Source) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | data "harbor_registry" "main" { 17 | name = "test_docker_harbor" 18 | } 19 | 20 | output "harbor_registry_id" { 21 | value = data.harbor_registry.main.id 22 | } 23 | ``` 24 | 25 | ## Schema 26 | 27 | ### Required 28 | 29 | - `name` (String) The name of the register. 30 | 31 | ### Read-Only 32 | 33 | - `description` (String) The description of the external container register. 34 | - `id` (String) The ID of this resource. 35 | - `insecure` (Boolean) If the certificate of the external container register can be verified. 36 | - `registry_id` (Number) The id of the register within harbor. 37 | - `status` (String) The health status of the external container register 38 | - `type` (String) The type of the provider type. 39 | - `url` (String) The url endpoint for the external container register 40 | -------------------------------------------------------------------------------- /docs/data-sources/robot_accounts.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_robot_accounts Data Source - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_robot_accounts (Data Source) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | data "harbor_robot_accounts" "example" { 17 | name = "example-robot" 18 | } 19 | 20 | output "robot_account_ids" { 21 | value = [data.harbor_robot_accounts.example.robot_accounts.*.id] 22 | } 23 | ``` 24 | 25 | ## Schema 26 | 27 | ### Optional 28 | 29 | - `name` (String) The name of the robot account to filter by. 30 | - `level` (String) Level of the robot account, currently either `system` or `project`. Default is `system`. 31 | - `project_id` (Number) The id of the project within harbor. 32 | 33 | ### Read-Only 34 | 35 | - `id` (String) The ID of this resource. 36 | - `robot_accounts` (List of Object) (see [below for nested schema](#nestedatt--robot_accounts)) 37 | 38 | 39 | 40 | ### Nested Schema for `robot_accounts` 41 | 42 | A list of robot accounts matching the previous arguments. Each `robot_account` object provides the attributes documented below. 43 | 44 | Read-Only: 45 | 46 | - `id` (String) The ID of the robot account. 47 | - `name` (String) The name of the robot account. 48 | - `description` (String) The description of the robot account. 49 | - `level` (String) The level of the robot account, currently either `system` or `project`. 50 | - `duration` (Number) The number of days before the robot account expires. A value of `-1` means the robot account will not expire. 51 | - `disable` (Boolean) Indicates whether the robot account is disabled. 52 | 53 | This data source retrieves a list of Harbor robot accounts and filters them based on the `name`, `level` and `project_id` arguments. It returns a list of `robot_account` objects, each containing the `id`, `name`, `description`, `level`, `duration` and `disable` attributes of a robot account that matches the filter criteria. 54 | -------------------------------------------------------------------------------- /docs/data-sources/users.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_users Data Source - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_users (Data Source) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | data "harbor_users" "example" { 17 | username = "example-user" 18 | } 19 | 20 | output "users_ids" { 21 | value = [data.harbor_users.example.users.*.id] 22 | } 23 | ``` 24 | 25 | ## Schema 26 | 27 | ### Optional 28 | 29 | - `username` (String) The name of the user to filter by. 30 | - `email` (String) The email of the user to filter by. 31 | 32 | ### Read-Only 33 | 34 | - `id` (String) The ID of this resource. 35 | - `users` (List of Object) (see [below for nested schema](#nestedatt--users)) 36 | 37 | 38 | 39 | ### Nested Schema for `users` 40 | 41 | A list of users matching the previous arguments. Each `user` object provides the attributes documented below. 42 | 43 | Read-Only: 44 | 45 | - `id` (String) The ID of the user. 46 | - `username` (String) The name of the user. 47 | - `full_name` (String) The full name of the user. 48 | - `email` (String) The email of the user. 49 | - `admin` (Boolean) Indicates whether the user is an admin. 50 | - `comment` (String) A comment about the user. 51 | 52 | This data source retrieves a list of Harbor users and filters them based on the `username` and `email` arguments. It returns a list of `user` objects, each containing the `id`, `username`, `full_name`, `email`, `admin` and `comment` attributes of a user that matches the filter criteria. 53 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor Provider" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor Provider 10 | 11 | 12 | 13 | ## Schema 14 | 15 | ### Required 16 | 17 | - `url` (String) The url of harbor 18 | - `password` (String) The password to be used to access harbor 19 | - `username` (String) The username to be used to access harbor 20 | 21 | ### Optional 22 | 23 | - `api_version` (Number) Choose which version of the api you would like to use 1 or 2 (default is 2) 24 | - `bearer_token` (String) The bearer token to be used to access harbor. Will take precedence over username and password if set 25 | - `session_id` (String) The session ID cookie (sid) when using OAuth/OIDC. Can be provided via `HARBOR_SESSION_ID`. Will take precedence over `bearer_token` if set. Note that OAuth/OIDC support is experimental and will be deprecated and removed if harbor provides a better way to authenticate with its API 26 | - `insecure` (Boolean) Choose to ignore certificate errors 27 | - `robot_prefix` (String) Without this option, the provider will try to automatically determine the robot prefix with a call to the admin api. If you don't have admin access and want to create system robot account, you'll have to set this value. 28 | 29 | ### Environment variables 30 | 31 | The provider can also be configured via environment variables: `HARBOR_URL`, `HARBOR_USERNAME`, `HARBOR_PASSWORD`, `HARBOR_SESSION_ID`. 32 | -------------------------------------------------------------------------------- /docs/resources/config_security.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_config_security Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_config_security (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "harbor_config_security" "main" { 17 | cve_allowlist = ["CVE-456", "CVE-123"] 18 | expires_at = "1701167767" 19 | } 20 | ``` 21 | 22 | ## Schema 23 | 24 | ### Required 25 | 26 | - `cve_allowlist` (List of String) System allowlist. Vulnerabilities in this list will be ignored when pushing and pulling images. Should be in the format or `["CVE-123", "CVE-145"]` or `["CVE-123"]` 27 | 28 | ### Optional 29 | 30 | - `expires_at` (Number) The time for expiration of the allowlist, in the form of seconds since epoch. This is an optional attribute, if it's not set the CVE allowlist does not expire. 31 | 32 | ### Read-Only 33 | 34 | - `creation_time` (String) Time of creation of the list. 35 | - `id` (String) The ID of this resource. 36 | - `update_time` (String) Time of update of the list. 37 | 38 | ## Import 39 | Import is supported using the following syntax with the `registry` `id`: 40 | 41 | ```shell 42 | # import using the id of the repo 43 | terraform import harbor_config_security.main "7" 44 | ``` 45 | Note that at this point of time Harbor doesn't has any api endpoint for deleting this list. Only updating the records. 46 | -------------------------------------------------------------------------------- /docs/resources/config_system.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_config_system Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_config_system (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "harbor_config_system" "main" { 17 | project_creation_restriction = "adminonly" 18 | robot_token_expiration = 30 19 | robot_name_prefix = "harbor@" 20 | storage_per_project = 100 21 | } 22 | ``` 23 | 24 | ## Schema 25 | 26 | ### Optional 27 | 28 | - `project_creation_restriction` (String) Who can create projects within Harbor. Can be `"adminonly"` or `"everyone"` 29 | - `read_only` (Boolean) Whether or not the system is in read only mode. 30 | - `robot_name_prefix` (String) Robot account prefix. 31 | - `robot_token_expiration` (Number) The amount of time in days a robot account will expire. 32 | - `scanner_skip_update_pulltime` (Boolean) Whether or not to skip update pull time for scanner. 33 | - `storage_per_project` (Number) Default quota space per project in GIB. Default is -1 (unlimited). 34 | - `audit_log_forward_endpoint` (String) The endpoint to forward audit logs to. 35 | - `skip_audit_log_database` (Boolean) Whether or not to skip audit log database. 36 | - `banner_message` (Block Set) (see [below for nested schema](#nestedblock--banner_message)) 37 | 38 | 39 | 40 | ### Nested Schema for `banner_message` 41 | 42 | ### Required 43 | - `message` (String) The message to display in the banner. 44 | 45 | ### Optional 46 | - `closable` (Boolean) Whether or not the banner message is closable. 47 | - `type` (String) The type of banner message. Can be `"info"`, `"warning"`, `"success"` or `"danger"`. 48 | - `from_date` (String) The date the banner message will start displaying. (Format: `MM/DD/YYYY`) 49 | - `to_date` (String) The date the banner message will stop displaying. (Format: `MM/DD/YYYY`) 50 | 51 | #### Notes 52 | `scanner_skip_update_pulltime` can only be used with harbor version v2.8.0 and above 53 | 54 | `robot_token_expiration` if the time is set to high you will get a 500 internal server error message when creating robot accounts 55 | 56 | ### Read-Only 57 | 58 | - `id` (String) The ID of this resource. 59 | -------------------------------------------------------------------------------- /docs/resources/garbage_collection.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_garbage_collection Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_garbage_collection (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "harbor_garbage_collection" "main" { 17 | schedule = "Daily" 18 | delete_untagged = true 19 | workers = 1 20 | } 21 | ``` 22 | 23 | ## Schema 24 | 25 | ### Required 26 | 27 | - `schedule` (String) Sets the schedule how often the Garbage Collection will run. Can be to `"hourly"`, `"daily"`, `"weekly"` or can be a custom cron string ie, `"0 5 4 * * *"` 28 | 29 | ### Optional 30 | 31 | - `delete_untagged` (Boolean) Allow garbage collection on untagged artifacts. 32 | - `workers` (Number) Number of workers to run the garbage collection, value must be between 1 and 5. 33 | 34 | ### Read-Only 35 | 36 | - `id` (String) The ID of this resource. 37 | -------------------------------------------------------------------------------- /docs/resources/group.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_group Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_group (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "harbor_group" "storage-group" { 17 | group_name = "storage-group" 18 | group_type = 3 19 | ldap_group_dn = "storage-group" 20 | } 21 | ``` 22 | 23 | ## Schema 24 | 25 | ### Required 26 | 27 | - `group_name` (String) The name of the group. 28 | - `group_type` (Number) 3. Note: group type 3 is OIDC group. 29 | 30 | ### Optional 31 | 32 | - `ldap_group_dn` (String) The distinguished name of the group within AD/LDAP. 33 | 34 | ### Read-Only 35 | 36 | - `id` (String) The ID of this resource. 37 | 38 | ## Import 39 | Import is supported using the following syntax with the `group` `id`: 40 | 41 | ```shell 42 | terraform import harbor_group.storage-group /usergroups/19 43 | ``` 44 | -------------------------------------------------------------------------------- /docs/resources/immutable_tag_rule.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_immutable_tag_rule Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_immutable_tag_rule (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "harbor_project" "main" { 17 | name = "acctest" 18 | } 19 | 20 | resource "harbor_immutable_tag_rule" "main" { 21 | disabled = true 22 | project_id = harbor_project.main.id 23 | repo_matching = "**" 24 | tag_matching = "v2.*" 25 | tag_excluding = "latest" 26 | } 27 | ``` 28 | 29 | ## Schema 30 | 31 | ### Required 32 | 33 | - `project_id` (String) The project id of which you would like to apply this policy. 34 | 35 | ### Optional 36 | 37 | - `disabled` (Boolean) Specify if the rule is disable or not. Defaults to `false` 38 | - `repo_excluding` (String) For the repositories excluding. 39 | - `repo_matching` (String) For the repositories matching. 40 | - `tag_excluding` (String) For the tag excluding. 41 | - `tag_matching` (String) For the tag matching. 42 | 43 | ### Read-Only 44 | 45 | - `id` (String) The ID of this resource. 46 | 47 | ## Import 48 | Import is supported using the following syntax with the `project` and `immutabletagrules` `id`'s: 49 | 50 | ```shell 51 | terraform import harbor_immutable_tag_rule.main /projects/4/immutabletagrules/25 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/resources/interrogation_services.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_interrogation_services Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_interrogation_services (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "harbor_interrogation_services" "main" { 17 | default_scanner = "Clair" 18 | vulnerability_scan_policy = "Daily" 19 | } 20 | ``` 21 | 22 | ## Schema 23 | 24 | ### Required 25 | 26 | - `vulnerability_scan_policy` (String) The frequency of the vulnerability scanning is done. This can be `Daily`, `Weekly`, `Monthly` or can be a custom cron string. 27 | 28 | ### Optional 29 | 30 | - `default_scanner` (String) Sets the default interrogation service `"Clair"` 31 | 32 | ### Read-Only 33 | 34 | - `id` (String) The ID of this resource. 35 | -------------------------------------------------------------------------------- /docs/resources/label.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_label Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_label (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ### Global 16 | 17 | ```terraform 18 | resource "harbor_label" "main" { 19 | name = "accTest" 20 | color = "#FF0000" 21 | description = "Description to for acceptance test" 22 | } 23 | ``` 24 | 25 | ### Project 26 | 27 | ```terraform 28 | resource "harbor_project" "main" { 29 | name = "acctest" 30 | } 31 | 32 | resource "harbor_label" "main" { 33 | name = "accTest" 34 | color = "#FFFFFF" 35 | description = "Description for acceptance test" 36 | project_id = harbor_project.main.id 37 | } 38 | ``` 39 | 40 | ## Schema 41 | 42 | ### Required 43 | 44 | - `name` (String) The of name of the label within harbor. 45 | 46 | ### Optional 47 | 48 | - `color` (String) The color of the label within harbor (Default: #FFFFF) 49 | - `description` (String) The Description of the label within harbor 50 | - `project_id` (String) The id of the project with harbor. 51 | 52 | ### Read-Only 53 | 54 | - `id` (String) The ID of this resource. 55 | - `scope` (String) 56 | 57 | ## Import 58 | Import is supported using the following syntax with the `label` `id`: 59 | 60 | ```shell 61 | terraform import harbor_label.main /labels/1 62 | ``` 63 | -------------------------------------------------------------------------------- /docs/resources/preheat_instance.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_preheat_instance Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_preheat_instance (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ### Basic Usage 16 | 17 | ```terraform 18 | resource "harbor_preheat_instance" "example" { 19 | name = "example-preheat-instance" 20 | vendor = "dragonfly" 21 | endpoint = "http://example.com" 22 | } 23 | ``` 24 | 25 | ### Usage with Authentication 26 | 27 | ```terraform 28 | resource "harbor_preheat_instance" "example" { 29 | name = "example-preheat-instance" 30 | vendor = "dragonfly" 31 | endpoint = "http://example.com" 32 | auth_mode = "BASIC" 33 | username = "example-user" 34 | password = "example-password" 35 | } 36 | ``` 37 | 38 | ## Schema 39 | 40 | ### Required 41 | 42 | - `name` (String) The name of the preheat instance. 43 | - `vendor` (String) The vendor of the preheat instance. Must be either "dragonfly" or "kraken". 44 | - `endpoint` (String) The endpoint of the preheat instance. 45 | 46 | ### Optional 47 | 48 | - `description` (String) The description of the preheat instance. Defaults to an empty string. 49 | - `auth_mode` (String) The authentication mode for the preheat instance. Must be either "NONE", "BASIC", or "OAUTH". Defaults to "NONE". 50 | - `username` (String) The username for the preheat instance. Required if `auth_mode` is "BASIC". Defaults to an empty string. 51 | - `password` (String, Sensitive) The password for the preheat instance. Required if `auth_mode` is "BASIC". Defaults to an empty string. 52 | - `token` (String, Sensitive) The token for the preheat instance. Required if `auth_mode` is "OAUTH". Defaults to an empty string. 53 | - `default` (Boolean) Whether the preheat instance is the default instance. Defaults to false. 54 | - `enabled` (Boolean) Whether the preheat instance is enabled. Defaults to true. 55 | - `insecure` (Boolean) Whether to allow insecure connections to the preheat instance. Defaults to false. 56 | 57 | ### Read-Only 58 | 59 | - `id` (String) The ID of the preheat instance. 60 | 61 | ## Import 62 | 63 | The `harbor_preheat_instance` resource can be imported using the preheat instance ID. 64 | 65 | ```sh 66 | terraform import harbor_preheat_instance.example /p2p/preheat/instances/example-preheat-instance 67 | ``` -------------------------------------------------------------------------------- /docs/resources/project.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_project Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_project (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ### Hosted 16 | 17 | ```terraform 18 | resource "harbor_project" "main" { 19 | name = "main" 20 | public = false # (Optional) Default value is false 21 | vulnerability_scanning = true # (Optional) Default value is true. Automatically scan images on push 22 | enable_content_trust = true # (Optional) Default value is false. Deny unsigned images from being pulled (notary) 23 | enable_content_trust_cosign = false # (Optional) Default value is false. Deny unsigned images from being pulled (cosign) 24 | auto_sbom_generation = true # (Optional) Default value is false. Automatically generate SBOMs for images 25 | } 26 | ``` 27 | 28 | ### Proxy 29 | 30 | ```terraform 31 | resource "harbor_project" "main" { 32 | name = "acctest" 33 | registry_id = harbor_registry.docker.registry_id 34 | } 35 | 36 | resource "harbor_registry" "docker" { 37 | provider_name = "docker-hub" 38 | name = "test" 39 | endpoint_url = "https://hub.docker.com" 40 | } 41 | ``` 42 | 43 | ## Schema 44 | 45 | ### Required 46 | 47 | - `name` (String) The name of the project that will be created in harbor. 48 | 49 | ### Optional 50 | 51 | - `cve_allowlist` (List of String) Project allowlist allows vulnerabilities in this list to be ignored in this project when pushing and pulling images. Should be in the format or `["CVE-123", "CVE-145"]` or `["CVE-123"]` 52 | - `deployment_security` (String) Prevent deployment of images with vulnerability severity equal or higher than the specified value. Images must be scanned before this takes effect. Possible values: `"critical"`, `"high"`, `"medium"`, `"low"`, `"none"`. (Default: `""` - empty) 53 | - `enable_content_trust` (Boolean) Enables Content Trust for project. When enabled it queries the embedded docker notary server. (Default: `false`). 54 | - `enable_content_trust_cosign` (Boolean) Enables Content Trust Cosign for project. When enabled it queries Cosign. (Default: `false`) 55 | - `force_destroy` (Boolean) A boolean that indicates all repositories should be deleted from the project so that the project can be destroyed without error. These repositories are *not* recoverable. 56 | - `public` (Boolean) The project will be public accessibility.(Default: `false`) 57 | - `registry_id` (Number) To enable project as Proxy Cache. 58 | - `storage_quota` (Number) The storage quota of the project in GB's. 59 | - `vulnerability_scanning` (Boolean) Images will be scanned for vulnerabilities when push to harbor. (Default: `true`) 60 | - `auto_sbom_generation` (Boolean) Automatically generate SBOM for images pushed to this project. (Default: `false`) can only be used with Harbor version v2.11.0 and above 61 | 62 | ### Read-Only 63 | 64 | - `id` (String) The ID of this resource. 65 | - `project_id` (Number) The project id of this resource. 66 | 67 | ## Import 68 | Import is supported using the following syntax with the `project` `id`: 69 | 70 | ```shell 71 | terraform import harbor_project.main /projects/1 72 | ``` 73 | -------------------------------------------------------------------------------- /docs/resources/project_member_group.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_project_member_group Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_project_member_group (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "harbor_project" "main" { 17 | name = "main" 18 | } 19 | 20 | resource "harbor_project_member_group" "main" { 21 | project_id = harbor_project.main.id 22 | group_name = "testing1" 23 | role = "projectadmin" 24 | type = "oidc" 25 | } 26 | ``` 27 | 28 | ## Schema 29 | 30 | ### Required 31 | 32 | - `project_id` (String) The project id of the project that the entity will have access to. 33 | - `role` (String) The permissions that the entity will be granted. 34 | - `type` (String) The group type. Can be set to `"ldap"`, `"internal"` or `"oidc"`. 35 | 36 | #### Notes 37 | `type` can only be `oidc` when used with harbor version v1.10.1 and above. 38 | 39 | ### Optional 40 | 41 | - `group_id` (Number) 3. Note: group type 3 is OIDC group. 42 | - `group_name` (String) The name of the group member entity. 43 | - `ldap_group_dn` (String) The distinguished name of the group within AD/LDAP. 44 | 45 | ### Read-Only 46 | 47 | - `id` (String) The ID of this resource. 48 | - `member_id` (Number) 49 | 50 | ## Import 51 | Import is supported using the following syntax with the `project` and `member` `id`'s: 52 | 53 | ```shell 54 | terraform import harbor_project_member_group.main /projects/10/members/200 55 | ``` 56 | -------------------------------------------------------------------------------- /docs/resources/project_member_user.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_project_member_user Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_project_member_user (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "harbor_project" "main" { 17 | name = "main" 18 | } 19 | 20 | resource "harbor_project_member_user" "main" { 21 | project_id = harbor_project.main.id 22 | user_name = "testing1" 23 | role = "projectadmin" 24 | } 25 | ``` 26 | 27 | ## Schema 28 | 29 | ### Required 30 | 31 | - `project_id` (String) The project id of the project that the entity will have access to. 32 | - `role` (String) The permissions that the entity will be granted. 33 | - `user_name` (String) The name of the member entity. 34 | 35 | ### Read-Only 36 | 37 | - `id` (String) The ID of this resource. 38 | - `member_id` (Number) The member id of the member. 39 | 40 | ## Import 41 | Import is supported using the following syntax with the `project` and `member` `id`'s: 42 | 43 | ```shell 44 | terraform import harbor_project_member_user.main /projects/10/members/200 45 | ``` 46 | -------------------------------------------------------------------------------- /docs/resources/project_webhook.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_project_webhook Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_project_webhook (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "harbor_project" "main" { 17 | name = "test-project" 18 | } 19 | 20 | resource "harbor_project_webhook" "main" { 21 | name = "test_webhook" 22 | address = "https://webhook.domain.com" 23 | project_id = harbor_project.main.id 24 | notify_type = "http" 25 | 26 | events_types = [ 27 | "DELETE_ARTIFACT", 28 | "PULL_ARTIFACT", 29 | "PUSH_ARTIFACT", 30 | "QUOTA_EXCEED", 31 | "QUOTA_WARNING", 32 | "REPLICATION", 33 | "SCANNING_FAILED", 34 | "SCANNING_COMPLETED", 35 | "TAG_RETENTION" 36 | ] 37 | 38 | } 39 | ``` 40 | 41 | ## Schema 42 | 43 | ### Required 44 | 45 | - `address` (String) The address of the webhook. 46 | - `events_types` (List of String) The type events you want to subscript to can be 47 | - `name` (String) The name of the webhook that will be created in harbor. 48 | - `notify_type` (String) The notification type either `http` or `slack`. 49 | - `project_id` (String) The project id of the harbor that webhook related to. 50 | 51 | ##### `events_types` Options 52 | 53 | - `DELETE_ARTIFACT` 54 | - `PULL_ARTIFACT` 55 | - `PUSH_ARTIFACT` 56 | - `QUOTA_EXCEED` 57 | - `QUOTA_WARNING` 58 | - `REPLICATION` 59 | - `SCANNING_FAILED` 60 | - `SCANNING_COMPLETED` 61 | - `TAG_RETENTION` 62 | 63 | ### Optional 64 | 65 | - `auth_header` (String) authentication header for you the webhook. 66 | - `description` (String) A description of the webhook. 67 | - `enabled` (Boolean) To enable / disable the webhook. Default `true`. 68 | - `skip_cert_verify` (Boolean) checks the for validate SSL certificate. 69 | 70 | ### Read-Only 71 | 72 | - `id` (String) The ID of this resource. 73 | -------------------------------------------------------------------------------- /docs/resources/purge_audit_log.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_purge_audit_log Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_purge_audit_log (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "harbor_purge_audit_log" "main" { 17 | schedule = "Daily" 18 | audit_retention_hour = 24 19 | include_operations = "create,pull" 20 | } 21 | ``` 22 | 23 | ## Schema 24 | 25 | ### Required 26 | 27 | - `audit_retention_hour` (Number) To configure how long audit logs should be kept. For example, if you set this to 24 Harbor will only purge audit logs that are 24 or more hours old. 28 | - `include_operations` (String) Valid values are `create` `delete` `pull`, thoses values can be comma separated. When Create, Delete, or Pull is set, Harbor will include audit logs for those operations in the purge. 29 | - `schedule` (String) Sets the schedule how often the Garbage Collection will run. Can be to `"Hourly"`, `"Daily"`, `"Weekly"` or can be a custom cron string ie, `"5 4 * * *"` 30 | 31 | ### Read-Only 32 | 33 | - `id` (String) The ID of this resource. 34 | -------------------------------------------------------------------------------- /docs/resources/registry.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_registry Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_registry (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "harbor_registry" "main" { 17 | provider_name = "docker-hub" 18 | name = "test_docker_harbor" 19 | endpoint_url = "https://hub.docker.com" 20 | } 21 | ``` 22 | 23 | ## Schema 24 | 25 | ### Required 26 | 27 | - `endpoint_url` (String) The url endpoint for the external container register ie `"https://hub.docker.com"` 28 | - `name` (String) The name of the register. 29 | - `provider_name` (String) The name of the provider. 30 | 31 | ##### `provider_name` Options 32 | 33 | - `alibaba` 34 | - `artifact-hub` 35 | - `aws` 36 | - `azure` 37 | - `docker-hub` 38 | - `docker-registry` 39 | - `gitlab` 40 | - `github` 41 | - `google` 42 | - `harbor` 43 | - `huawei` 44 | - `jfrog` 45 | - `quay` 46 | 47 | ### Optional 48 | 49 | - `access_id` (String) The username / access id for the external container register. 50 | - `access_secret` (String, Sensitive) The password / access keys / token for the external container register. 51 | - `description` (String) The description of the external container register. 52 | - `insecure` (Boolean) Verifies the certificate of the external container register. (Default: `false`) 53 | 54 | ### Read-Only 55 | 56 | - `id` (String) The ID of this resource. 57 | - `registry_id` (Number) 58 | - `status` (String) 59 | 60 | ## Import 61 | Import is supported using the following syntax with the `registry` `id`: 62 | 63 | ```shell 64 | terraform import harbor_registry.main /registries/7 65 | ``` 66 | -------------------------------------------------------------------------------- /docs/resources/retention_policy.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_retention_policy Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_retention_policy (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "harbor_project" "main" { 17 | name = "acctest" 18 | } 19 | 20 | resource "harbor_retention_policy" "main" { 21 | scope = harbor_project.main.id 22 | schedule = "Daily" 23 | rule { 24 | n_days_since_last_pull = 5 25 | repo_matching = "**" 26 | tag_matching = "latest" 27 | } 28 | rule { 29 | n_days_since_last_push = 10 30 | repo_matching = "**" 31 | tag_matching = "{latest,snapshot}" 32 | } 33 | } 34 | ``` 35 | 36 | ## Schema 37 | 38 | ### Required 39 | 40 | - `rule` (Block List, Min: 1, Max: 15) (see [below for nested schema](#nestedblock--rule)) 41 | - `scope` (String) The project id of which you would like to apply this policy. 42 | 43 | ### Optional 44 | 45 | - `schedule` (String) The schedule of when you would like the policy to run. This can be `Hourly`, `Daily`, `Weekly` or can be a custom cron string. 46 | 47 | ### Read-Only 48 | 49 | - `id` (String) The ID of this resource. 50 | 51 | 52 | 53 | ### Nested Schema for `rule` 54 | 55 | ~> Multiple tags or repositories must be provided as a comma-separated list wrapped into curly brackets `{ }`. Otherwise, the value is interpreted as a single value. 56 | 57 | Optional: 58 | 59 | - `always_retain` (Boolean) retain always. 60 | - `disabled` (Boolean) Specify if the rule is disable or not. Defaults to `false` 61 | - `most_recently_pulled` (Number) retain the most recently pulled n artifacts. 62 | - `most_recently_pushed` (Number) retain the most recently pushed n artifacts. 63 | - `n_days_since_last_pull` (Number) retains the artifacts pulled within the lasts n days. 64 | - `n_days_since_last_push` (Number) retains the artifacts pushed within the lasts n days. 65 | - `repo_excluding` (String) For the repositories excluding. 66 | - `repo_matching` (String) For the repositories matching. 67 | - `tag_excluding` (String) For the tag excluding. 68 | - `tag_matching` (String) For the tag matching. 69 | - `untagged_artifacts` (Boolean) with untagged artifacts. Defaults to `true` 70 | 71 | ## Import 72 | Import is supported using the following syntax with the `retention_policy` `id`: 73 | 74 | ```shell 75 | terraform import harbor_retention_policy.main /retentions/10 76 | ``` 77 | -------------------------------------------------------------------------------- /docs/resources/tasks.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_tasks Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_tasks (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "harbor_tasks" "main" { 17 | vulnerability_scan_policy = "daily" 18 | } 19 | ``` 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `vulnerability_scan_policy` (String) The frequency of the vulnerability scanning is done. Can be to **"hourly"**, **"daily"** or **"weekly"** 26 | 27 | ### Read-Only 28 | 29 | - `id` (String) The ID of this resource. 30 | -------------------------------------------------------------------------------- /docs/resources/user.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_user Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # harbor_user (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "harbor_user" "main" { 17 | username = "john" 18 | password = "Password12345!" 19 | full_name = "John Smith" 20 | email = "john@smith.com" 21 | } 22 | ``` 23 | 24 | ## Schema 25 | 26 | ### Required 27 | 28 | - `email` (String) The email address of the internal user. 29 | - `full_name` (String) The Full Name of the internal user. 30 | - `password` (String, Sensitive) The password for the internal user. 31 | - `username` (String) The username of the internal user. 32 | 33 | ### Optional 34 | 35 | - `admin` (Boolean) If the user will have admin rights within Harbor (Default: `false`) 36 | - `comment` (String) Any comments for that are need for the internal user. 37 | 38 | ### Read-Only 39 | 40 | - `id` (String) The ID of this resource. 41 | 42 | ## Import 43 | Import is supported using the following syntax with the `user` `id`: 44 | 45 | ```shell 46 | terraform import harbor_user.main /users/19 47 | ``` 48 | -------------------------------------------------------------------------------- /examples/data-sources/harbor_groups/data-source.tf: -------------------------------------------------------------------------------- 1 | data "harbor_groups" "example" { 2 | group_name = "example-group" 3 | } 4 | 5 | output "group_ids" { 6 | value = [data.harbor_groups.example.*.id] 7 | } 8 | -------------------------------------------------------------------------------- /examples/data-sources/harbor_project/data-source.tf: -------------------------------------------------------------------------------- 1 | data "harbor_project" "main" { 2 | name = "library" 3 | } 4 | 5 | output "project_id" { 6 | value = data.harbor_project.main.id 7 | } 8 | -------------------------------------------------------------------------------- /examples/data-sources/harbor_project_member_groups/data-source.tf: -------------------------------------------------------------------------------- 1 | data "harbor_project_member_groups" "example" { 2 | project_id = "1" 3 | } 4 | 5 | output "project_member_group_ids" { 6 | value = [data.harbor_project_member_groups.example.project_member_groups.*.id] 7 | } 8 | -------------------------------------------------------------------------------- /examples/data-sources/harbor_project_member_users/data-source.tf: -------------------------------------------------------------------------------- 1 | data "harbor_project_member_users" "example" { 2 | project_id = "1" 3 | } 4 | 5 | output "project_member_user_ids" { 6 | value = [data.harbor_project_member_users.example.project_member_users.*.id] 7 | } 8 | -------------------------------------------------------------------------------- /examples/data-sources/harbor_projects/data-source.tf: -------------------------------------------------------------------------------- 1 | data "harbor_projects" "proxycache" { 2 | type = "ProxyCache" 3 | } 4 | 5 | output "proxy_cache_projects" { 6 | value = data.harbor_projects.proxycache 7 | } 8 | -------------------------------------------------------------------------------- /examples/data-sources/harbor_registry/data-source.tf: -------------------------------------------------------------------------------- 1 | data "harbor_registry" "main" { 2 | name = "test_docker_harbor" 3 | } 4 | 5 | output "harbor_registry_id" { 6 | value = data.harbor_registry.main.id 7 | } 8 | -------------------------------------------------------------------------------- /examples/data-sources/harbor_robot_accounts/data-source.tf: -------------------------------------------------------------------------------- 1 | data "harbor_robot_accounts" "example" { 2 | name = "example-robot" 3 | } 4 | 5 | output "robot_account_ids" { 6 | value = [data.harbor_robot_accounts.example.robot_accounts.*.id] 7 | } 8 | -------------------------------------------------------------------------------- /examples/data-sources/harbor_users/data-source.tf: -------------------------------------------------------------------------------- 1 | data "harbor_users" "example" { 2 | username = "example-user" 3 | } 4 | 5 | output "users_ids" { 6 | value = [data.harbor_users.example.users.*.id] 7 | } 8 | -------------------------------------------------------------------------------- /examples/provider/provider.tf: -------------------------------------------------------------------------------- 1 | provider "harbor" { 2 | url = "https://harbor.aceme_corpartion.com" 3 | username = "insert_admin_username_here" 4 | password = "insert_password_here" 5 | bearer_token = "insert_bearer_token_here" 6 | insecure = true 7 | api_version = 2 8 | } 9 | -------------------------------------------------------------------------------- /examples/resources/harbor_config_auth/ldap.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_config_auth" "ldap" { 2 | auth_mode = "ldap_auth" 3 | primary_auth_mode = true 4 | ldap_url = "openldap.default.svc.cluster.local:389" 5 | ldap_search_dn = "cn=admin,dc=example,dc=org" 6 | ldap_search_password = "Not@SecurePassw0rd" 7 | ldap_base_dn = "dc=example,dc=org" 8 | ldap_uid = "email" 9 | ldap_verify_cert = false 10 | } 11 | -------------------------------------------------------------------------------- /examples/resources/harbor_config_auth/oidc.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_config_auth" "oidc" { 2 | auth_mode = "oidc_auth" 3 | primary_auth_mode = true 4 | oidc_name = "azure" 5 | oidc_endpoint = "https://login.microsoftonline.com/{GUID goes here}/v2.0" 6 | oidc_client_id = "OIDC Client ID goes here" 7 | oidc_client_secret = "ODDC Client Secret goes here" 8 | oidc_scope = "openid,email" 9 | oidc_verify_cert = true 10 | oidc_auto_onboard = true 11 | oidc_user_claim = "name" 12 | oidc_admin_group = "administrators" 13 | } 14 | -------------------------------------------------------------------------------- /examples/resources/harbor_config_security/resource.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_config_security" "main" { 2 | cve_allowlist = ["CVE-456", "CVE-123"] 3 | expires_at = "1701167767" 4 | } 5 | -------------------------------------------------------------------------------- /examples/resources/harbor_config_system/resource.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_config_system" "main" { 2 | project_creation_restriction = "adminonly" 3 | robot_token_expiration = 30 4 | robot_name_prefix = "harbor@" 5 | storage_per_project = 100 6 | } 7 | -------------------------------------------------------------------------------- /examples/resources/harbor_garbage_collection/resource.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_garbage_collection" "main" { 2 | schedule = "Daily" 3 | delete_untagged = true 4 | workers = 1 5 | } 6 | -------------------------------------------------------------------------------- /examples/resources/harbor_group/resource.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_group" "storage-group" { 2 | group_name = "storage-group" 3 | group_type = 3 4 | ldap_group_dn = "storage-group" 5 | } 6 | -------------------------------------------------------------------------------- /examples/resources/harbor_immutable_tag_rule/resource.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_project" "main" { 2 | name = "acctest" 3 | } 4 | 5 | resource "harbor_immutable_tag_rule" "main" { 6 | disabled = true 7 | project_id = harbor_project.main.id 8 | repo_matching = "**" 9 | tag_matching = "v2.*" 10 | tag_excluding = "latest" 11 | } 12 | -------------------------------------------------------------------------------- /examples/resources/harbor_interrogation_services/resource.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_interrogation_services" "main" { 2 | default_scanner = "Clair" 3 | vulnerability_scan_policy = "Daily" 4 | } 5 | -------------------------------------------------------------------------------- /examples/resources/harbor_label/global.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_label" "main" { 2 | name = "accTest" 3 | color = "#FF0000" 4 | description = "Description to for acceptance test" 5 | } -------------------------------------------------------------------------------- /examples/resources/harbor_label/project.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_project" "main" { 2 | name = "acctest" 3 | } 4 | 5 | resource "harbor_label" "main" { 6 | name = "accTest" 7 | color = "#FFFFFF" 8 | description = "Description for acceptance test" 9 | project_id = harbor_project.main.id 10 | } 11 | -------------------------------------------------------------------------------- /examples/resources/harbor_preheat_instance/authentification.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_preheat_instance" "example" { 2 | name = "example-preheat-instance" 3 | vendor = "dragonfly" 4 | endpoint = "http://example.com" 5 | auth_mode = "BASIC" 6 | username = "example-user" 7 | password = "example-password" 8 | } 9 | -------------------------------------------------------------------------------- /examples/resources/harbor_preheat_instance/basic.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_preheat_instance" "example" { 2 | name = "example-preheat-instance" 3 | vendor = "dragonfly" 4 | endpoint = "http://example.com" 5 | } 6 | -------------------------------------------------------------------------------- /examples/resources/harbor_project/proxy.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_project" "main" { 2 | name = "acctest" 3 | registry_id = harbor_registry.docker.registry_id 4 | } 5 | 6 | resource "harbor_registry" "docker" { 7 | provider_name = "docker-hub" 8 | name = "test" 9 | endpoint_url = "https://hub.docker.com" 10 | } 11 | -------------------------------------------------------------------------------- /examples/resources/harbor_project/resource.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_project" "main" { 2 | name = "main" 3 | public = false # (Optional) Default value is false 4 | vulnerability_scanning = true # (Optional) Default value is true. Automatically scan images on push 5 | enable_content_trust = true # (Optional) Default value is false. Deny unsigned images from being pulled (notary) 6 | enable_content_trust_cosign = false # (Optional) Default value is false. Deny unsigned images from being pulled (cosign) 7 | auto_sbom_generation = true # (Optional) Default value is false. Automatically generate SBOMs for images 8 | } 9 | -------------------------------------------------------------------------------- /examples/resources/harbor_project_member_group/resource.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_project" "main" { 2 | name = "main" 3 | } 4 | 5 | resource "harbor_project_member_group" "main" { 6 | project_id = harbor_project.main.id 7 | group_name = "testing1" 8 | role = "projectadmin" 9 | type = "oidc" 10 | } 11 | -------------------------------------------------------------------------------- /examples/resources/harbor_project_member_user/resource.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_project" "main" { 2 | name = "main" 3 | } 4 | 5 | resource "harbor_project_member_user" "main" { 6 | project_id = harbor_project.main.id 7 | user_name = "testing1" 8 | role = "projectadmin" 9 | } 10 | -------------------------------------------------------------------------------- /examples/resources/harbor_project_webhook/resource.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_project" "main" { 2 | name = "test-project" 3 | } 4 | 5 | resource "harbor_project_webhook" "main" { 6 | name = "test_webhook" 7 | address = "https://webhook.domain.com" 8 | project_id = harbor_project.main.id 9 | notify_type = "http" 10 | 11 | events_types = [ 12 | "DELETE_ARTIFACT", 13 | "PULL_ARTIFACT", 14 | "PUSH_ARTIFACT", 15 | "QUOTA_EXCEED", 16 | "QUOTA_WARNING", 17 | "REPLICATION", 18 | "SCANNING_FAILED", 19 | "SCANNING_COMPLETED", 20 | "TAG_RETENTION" 21 | ] 22 | 23 | } 24 | -------------------------------------------------------------------------------- /examples/resources/harbor_purge_audit_log/resource.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_purge_audit_log" "main" { 2 | schedule = "Daily" 3 | audit_retention_hour = 24 4 | include_operations = "create,pull" 5 | } 6 | -------------------------------------------------------------------------------- /examples/resources/harbor_registry/resource.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_registry" "main" { 2 | provider_name = "docker-hub" 3 | name = "test_docker_harbor" 4 | endpoint_url = "https://hub.docker.com" 5 | } 6 | -------------------------------------------------------------------------------- /examples/resources/harbor_replication/resource.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_registry" "main" { 2 | provider_name = "docker-hub" 3 | name = "test_docker_harbor" 4 | endpoint_url = "https://hub.docker.com" 5 | 6 | } 7 | 8 | resource "harbor_replication" "push" { 9 | name = "test_push" 10 | action = "push" 11 | registry_id = harbor_registry.main.registry_id 12 | } 13 | 14 | resource "harbor_replication" "alpine" { 15 | name = "alpine" 16 | action = "pull" 17 | registry_id = harbor_registry.main.registry_id 18 | schedule = "0 0/15 * * * *" 19 | filters { 20 | name = "library/alpine" 21 | } 22 | filters { 23 | tag = "3.*.*" 24 | } 25 | filters { 26 | resource = "artifact" 27 | } 28 | filters { 29 | labels = ["qa"] 30 | } 31 | } 32 | 33 | resource "harbor_replication" "alpine" { 34 | name = "alpine" 35 | action = "push" 36 | registry_id = harbor_registry.main.registry_id 37 | schedule = "event_based" 38 | filters { 39 | name = "library/alpine" 40 | } 41 | filters { 42 | tag = "3.*.*" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/resources/harbor_retention_policy/resource.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_project" "main" { 2 | name = "acctest" 3 | } 4 | 5 | resource "harbor_retention_policy" "main" { 6 | scope = harbor_project.main.id 7 | schedule = "Daily" 8 | rule { 9 | n_days_since_last_pull = 5 10 | repo_matching = "**" 11 | tag_matching = "latest" 12 | } 13 | rule { 14 | n_days_since_last_push = 10 15 | repo_matching = "**" 16 | tag_matching = "{latest,snapshot}" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/resources/harbor_robot_account/project.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_project" "main" { 2 | name = "main" 3 | } 4 | 5 | resource "harbor_robot_account" "project" { 6 | name = "example-project" 7 | description = "project level robot account" 8 | level = "project" 9 | permissions { 10 | access { 11 | action = "pull" 12 | resource = "repository" 13 | } 14 | access { 15 | action = "push" 16 | resource = "repository" 17 | } 18 | kind = "project" 19 | namespace = harbor_project.main.name 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/resources/harbor_robot_account/resource.tf: -------------------------------------------------------------------------------- 1 | resource "random_password" "password" { 2 | length = 12 3 | special = false 4 | } 5 | 6 | resource "harbor_project" "main" { 7 | name = "main" 8 | } 9 | 10 | resource "harbor_robot_account" "system" { 11 | name = "example-system" 12 | description = "system level robot account" 13 | level = "system" 14 | secret = resource.random_password.password.result 15 | permissions { 16 | access { 17 | action = "create" 18 | resource = "label" 19 | } 20 | kind = "system" 21 | namespace = "/" 22 | } 23 | permissions { 24 | access { 25 | action = "push" 26 | resource = "repository" 27 | } 28 | kind = "project" 29 | namespace = harbor_project.main.name 30 | } 31 | permissions { 32 | access { 33 | action = "pull" 34 | resource = "repository" 35 | } 36 | kind = "project" 37 | namespace = "*" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/resources/harbor_tasks/resource.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_tasks" "main" { 2 | vulnerability_scan_policy = "daily" 3 | } 4 | -------------------------------------------------------------------------------- /examples/resources/harbor_user/resource.tf: -------------------------------------------------------------------------------- 1 | resource "harbor_user" "main" { 2 | username = "john" 3 | password = "Password12345!" 4 | full_name = "John Smith" 5 | email = "john@smith.com" 6 | } 7 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/goharbor/terraform-provider-harbor 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.2 6 | 7 | require github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.1 8 | 9 | require ( 10 | github.com/ProtonMail/go-crypto v1.1.3 // indirect 11 | github.com/agext/levenshtein v1.2.3 // indirect 12 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect 13 | github.com/cloudflare/circl v1.3.7 // indirect 14 | github.com/fatih/color v1.16.0 // indirect 15 | github.com/golang/protobuf v1.5.4 // indirect 16 | github.com/google/go-cmp v0.6.0 // indirect 17 | github.com/hashicorp/errwrap v1.1.0 // indirect 18 | github.com/hashicorp/go-checkpoint v0.5.0 // indirect 19 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 20 | github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect 21 | github.com/hashicorp/go-hclog v1.6.3 // indirect 22 | github.com/hashicorp/go-multierror v1.1.1 // indirect 23 | github.com/hashicorp/go-plugin v1.6.2 // indirect 24 | github.com/hashicorp/go-retryablehttp v0.7.7 // indirect 25 | github.com/hashicorp/go-uuid v1.0.3 // indirect 26 | github.com/hashicorp/go-version v1.7.0 // indirect 27 | github.com/hashicorp/hc-install v0.9.1 // indirect 28 | github.com/hashicorp/hcl/v2 v2.23.0 // indirect 29 | github.com/hashicorp/logutils v1.0.0 // indirect 30 | github.com/hashicorp/terraform-exec v0.22.0 // indirect 31 | github.com/hashicorp/terraform-json v0.24.0 // indirect 32 | github.com/hashicorp/terraform-plugin-go v0.26.0 // indirect 33 | github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect 34 | github.com/hashicorp/terraform-registry-address v0.2.4 // indirect 35 | github.com/hashicorp/terraform-svchost v0.1.1 // indirect 36 | github.com/hashicorp/yamux v0.1.1 // indirect 37 | github.com/mattn/go-colorable v0.1.13 // indirect 38 | github.com/mattn/go-isatty v0.0.20 // indirect 39 | github.com/mitchellh/copystructure v1.2.0 // indirect 40 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect 41 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect 42 | github.com/mitchellh/mapstructure v1.5.0 // indirect 43 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 44 | github.com/oklog/run v1.1.0 // indirect 45 | github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect 46 | github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect 47 | github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect 48 | github.com/zclconf/go-cty v1.16.2 // indirect 49 | golang.org/x/crypto v0.36.0 // indirect 50 | golang.org/x/mod v0.22.0 // indirect 51 | golang.org/x/net v0.38.0 // indirect 52 | golang.org/x/sync v0.12.0 // indirect 53 | golang.org/x/sys v0.31.0 // indirect 54 | golang.org/x/text v0.23.0 // indirect 55 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect 56 | google.golang.org/appengine v1.6.8 // indirect 57 | google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect 58 | google.golang.org/grpc v1.69.4 // indirect 59 | google.golang.org/protobuf v1.36.3 // indirect 60 | ) 61 | -------------------------------------------------------------------------------- /kind-cluster.yaml: -------------------------------------------------------------------------------- 1 | kind: Cluster 2 | apiVersion: kind.x-k8s.io/v1alpha4 3 | nodes: 4 | - role: control-plane 5 | kubeadmConfigPatches: 6 | - | 7 | kind: InitConfiguration 8 | nodeRegistration: 9 | kubeletExtraArgs: 10 | node-labels: "ingress-ready=true" 11 | extraPortMappings: 12 | - containerPort: 80 13 | hostPort: 80 14 | protocol: TCP 15 | - containerPort: 443 16 | hostPort: 443 17 | protocol: TCP 18 | - containerPort: 30003 19 | hostPort: 30003 20 | protocol: TCP -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/goharbor/terraform-provider-harbor/provider" 5 | "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" 6 | ) 7 | 8 | func main() { 9 | plugin.Serve(&plugin.ServeOpts{ 10 | ProviderFunc: provider.Provider}) 11 | } 12 | -------------------------------------------------------------------------------- /models/group.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | var PathGroups = "/usergroups" 4 | 5 | // 6 | type GroupBody struct { 7 | Groupname string `json:"group_name,omitempty"` 8 | GroupType int `json:"group_type,omitempty"` 9 | LdapGroupDn string `json:"ldap_group_dn,omitempty"` 10 | ID int `json:"id,omitempty"` 11 | } 12 | -------------------------------------------------------------------------------- /models/headers.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type ResponseHeaders struct { 4 | Connection []string `json:"Connection,omitempty"` 5 | ContentLength []string `json:"Content-Length,omitempty"` 6 | ContentType []string `json:"Content-Type,omitempty"` 7 | Date []string `json:"Date,omitempty"` 8 | Location []string `json:"Location,omitempty"` 9 | Server []string `json:"Server,omitempty"` 10 | SetCookie []string `json:"Set-Cookie,omitempty"` 11 | XRequestID []string `json:"X-Request-Id,omitempty"` 12 | } 13 | -------------------------------------------------------------------------------- /models/immutable_tag_rule.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | var PathImmutableTagRules = "/immutabletagrules" 4 | 5 | type ImmutableTagRule struct { 6 | Id int `json:"id,omitempty"` 7 | Disabled bool `json:"disabled"` 8 | ScopeSelectors ScopeSelectors `json:"scope_selectors"` 9 | ImmutableTagRuleTagSelectors []ImmutableTagRuleTagSelectors `json:"tag_selectors"` 10 | Action string `json:"action"` 11 | Template string `json:"template"` 12 | } 13 | 14 | type ImmutableTagRuleTagSelectors struct { 15 | Kind string `json:"kind"` 16 | Decoration string `json:"decoration"` 17 | Pattern string `json:"pattern"` 18 | } 19 | -------------------------------------------------------------------------------- /models/interogations.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type InterogationsBodyResponse struct { 4 | Schedule struct { 5 | Type string `json:"type,omitempty"` 6 | Cron string `json:"cron,omitempty"` 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /models/labels.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | var PathLabel = "/labels" 4 | 5 | type Labels struct { 6 | Description string `json:"description"` 7 | Color string `json:"color"` 8 | Deleted bool `json:"deleted"` 9 | Scope string `json:"scope"` 10 | ProjectID int `json:"project_id"` 11 | ID int `json:"id"` 12 | Name string `json:"name"` 13 | } 14 | -------------------------------------------------------------------------------- /models/preheat_instance.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | var PathPreheatInstance = "/p2p/preheat/instances" 4 | 5 | type PreheatInstance struct { 6 | ID int `json:"id"` 7 | Name string `json:"name"` 8 | Description string `json:"description"` 9 | Vendor string `json:"vendor"` 10 | Endpoint string `json:"endpoint"` 11 | AuthMode string `json:"auth_mode"` 12 | AuthInfo PreheatInstanceAuthInfo `json:"auth_info"` 13 | Status string `json:"status"` 14 | Enabled bool `json:"enabled"` 15 | Default bool `json:"default"` 16 | Insecure bool `json:"insecure"` 17 | SetupTimestamp int64 `json:"setup_timestamp"` 18 | } 19 | 20 | type PreheatInstanceAuthInfo struct { 21 | Token string `json:"token,omitempty"` 22 | Username string `json:"username,omitempty"` 23 | Password string `json:"password,omitempty"` 24 | } 25 | -------------------------------------------------------------------------------- /models/project_members.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type ProjectMembersBodyPost struct { 4 | ID int `json:"id,omitempty"` 5 | RoleID int `json:"role_id,omitempty"` 6 | GroupMember ProjectMembersBodyGroup `json:"member_group,omitempty"` 7 | UserMembers ProjectMemberUsersGroup `json:"member_user,omitempty"` 8 | } 9 | 10 | type ProjectMembersBodyResponses struct { 11 | ID int `json:"id,omitempty"` 12 | RoleID int `json:"role_id,omitempty"` 13 | ProjectID int `json:"project_id,omitempty"` 14 | EntityType string `json:"entity_type,omitempty"` 15 | EntityName string `json:"entity_name,omitempty"` 16 | } 17 | 18 | type ProjectMembersBodyGroup struct { 19 | GroupType int `json:"group_type,omitempty"` 20 | GroupName string `json:"group_name,omitempty"` 21 | GroupID int `json:"id,omitempty"` 22 | LdapGroupDN string `json:"ldap_group_dn,omitempty"` 23 | } 24 | 25 | type ProjectMemberUsersGroup struct { 26 | UserName string `json:"username,omitempty"` 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /models/project_webhooks.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "time" 4 | 5 | type ProjectWebhook struct { 6 | UpdateTime time.Time `json:"update_time"` 7 | Description string `json:"description"` 8 | Creator string `json:"creator"` 9 | CreationTime time.Time `json:"creation_time"` 10 | Enabled bool `json:"enabled"` 11 | Targets []WebHookTargets `json:"targets"` 12 | EventTypes []interface{} `json:"event_types"` 13 | ProjectID int `json:"project_id"` 14 | ID int `json:"id"` 15 | Name string `json:"name"` 16 | } 17 | type WebHookTargets struct { 18 | Type string `json:"type"` 19 | AuthHeader string `json:"auth_header"` 20 | SkipCertVerify bool `json:"skip_cert_verify"` 21 | Address string `json:"address"` 22 | } 23 | -------------------------------------------------------------------------------- /models/projects.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | var PathProjects = "/projects" 4 | 5 | type ProjectsBodyPost struct { 6 | CountLimit int `json:"count_limit,omitempty"` 7 | ProjectName string `json:"project_name,omitempty"` 8 | RegistryID int `json:"registry_id,omitempty"` 9 | CveAllowlist struct { 10 | Items CveAllowlistItems `json:"items,omitempty"` 11 | // Items []struct { 12 | // CveID string `json:"cve_id,omitempty"` 13 | // } `json:"items,omitempty"` 14 | ProjectID int `json:"project_id,omitempty"` 15 | ID int `json:"id,omitempty"` 16 | ExpiresAt int `json:"expires_at,omitempty"` 17 | } `json:"cve_allowlist,omitempty"` 18 | StorageLimit int `json:"storage_limit,omitempty"` 19 | Metadata struct { 20 | EnableContentTrust string `json:"enable_content_trust,omitempty"` 21 | EnableContentTrustCosign string `json:"enable_content_trust_cosign,omitempty"` 22 | AutoScan string `json:"auto_scan,omitempty"` 23 | Severity string `json:"severity,omitempty"` 24 | ReuseSysCveAllowlist string `json:"reuse_sys_cve_allowlist,omitempty"` 25 | Public string `json:"public,omitempty"` 26 | PreventVul string `json:"prevent_vul,omitempty"` 27 | AutoSbomGeneration string `json:"auto_sbom_generation,omitempty"` 28 | } `json:"metadata,omitempty"` 29 | } 30 | 31 | type ProjectsBodyResponses struct { 32 | UpdateTime string `json:"update_time"` 33 | OwnerName string `json:"owner_name"` 34 | Name string `json:"name"` 35 | Deleted bool `json:"deleted"` 36 | OwnerID int `json:"owner_id"` 37 | RepoCount int `json:"repo_count"` 38 | RegistryID *int `json:"registry_id"` 39 | CreationTime string `json:"creation_time"` 40 | Togglable bool `json:"togglable"` 41 | ProjectID int `json:"project_id"` 42 | CurrentUserRoleID int `json:"current_user_role_id"` 43 | CurrentUserRoleIds []int `json:"current_user_role_ids"` 44 | ChartCount int `json:"chart_count"` 45 | CveAllowlist struct { 46 | Items []struct { 47 | CveID string `json:"cve_id"` 48 | } `json:"items"` 49 | ProjectID int `json:"project_id"` 50 | ID int `json:"id"` 51 | ExpiresAt int `json:"expires_at"` 52 | } `json:"cve_allowlist"` 53 | Metadata struct { 54 | EnableContentTrust string `json:"enable_content_trust,omitempty"` 55 | EnableContentTrustCosign string `json:"enable_content_trust_cosign,omitempty"` 56 | AutoScan string `json:"auto_scan,omitempty"` 57 | Severity string `json:"severity"` 58 | ReuseSysCveAllowlist string `json:"reuse_sys_cve_allowlist"` 59 | Public string `json:"public"` 60 | PreventVul string `json:"prevent_vul"` 61 | RetentionId string `json:"retention_id"` 62 | AutoSbomGeneration string `json:"auto_sbom_generation,omitempty"` 63 | } `json:"metadata"` 64 | } 65 | 66 | type CveAllowlistItems []struct { 67 | CveID string `json:"cve_id,omitempty"` 68 | } 69 | type CveAllowlistItem struct { 70 | CveID string `json:"cve_id,omitempty"` 71 | } 72 | -------------------------------------------------------------------------------- /models/quota.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "time" 4 | 5 | type StorageQuota struct { 6 | Hard Hard `json:"hard"` 7 | } 8 | type Hard struct { 9 | Storage int64 `json:"storage"` 10 | } 11 | type Used struct { 12 | Storage int64 `json:"storage"` 13 | } 14 | type QuotaResponse struct { 15 | CreationTime time.Time `json:"creation_time"` 16 | Hard Hard `json:"hard"` 17 | ID int `json:"id"` 18 | Ref struct { 19 | ID int `json:"id"` 20 | Name string `json:"name"` 21 | Owner string `json:"owner"` 22 | } `json:"ref"` 23 | UpdateTime time.Time `json:"update_time"` 24 | Used Used `json:"used"` 25 | } 26 | -------------------------------------------------------------------------------- /models/registry.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | var PathRegistries = "/registries" 4 | 5 | type RegistryBody struct { 6 | Status string `json:"status,omitempty"` 7 | Credential struct { 8 | AccessKey string `json:"access_key,omitempty"` 9 | AccessSecret string `json:"access_secret,omitempty"` 10 | Type string `json:"type,omitempty"` 11 | } `json:"credential,omitempty"` 12 | UpdateTime string `json:"update_time,omitempty"` 13 | Name string `json:"name,omitempty"` 14 | URL string `json:"url,omitempty"` 15 | Insecure bool `json:"insecure,omitempty"` 16 | CreationTime string `json:"creation_time,omitempty"` 17 | Type string `json:"type,omitempty"` 18 | ID int `json:"id,omitempty"` 19 | Description string `json:"description,omitempty"` 20 | } 21 | 22 | type RegistryUpdateBody struct { 23 | AccessKey string `json:"access_key"` 24 | CredentialType string `json:"credential_type,omitempty"` 25 | Name string `json:"name,omitempty"` 26 | AccessSecret string `json:"access_secret"` 27 | URL string `json:"url,omitempty"` 28 | Insecure bool `json:"insecure"` 29 | Description string `json:"description"` 30 | } 31 | -------------------------------------------------------------------------------- /models/replications.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | var PathReplication = "/replication/policies" 4 | var PathExecution = "/replication/executions" 5 | 6 | type ReplicationBody struct { 7 | Name string `json:"name,omitempty"` 8 | Description string `json:"description,omitempty"` 9 | ID int `json:"id"` 10 | SrcRegistry struct { 11 | ID int `json:"id,omitempty"` 12 | } `json:"src_registry,omitempty"` 13 | DestRegistry struct { 14 | ID int `json:"id,omitempty"` 15 | } `json:"dest_registry,omitempty"` 16 | DestNamespace string `json:"dest_namespace,omitempty"` 17 | DestNamespaceReplace int `json:"dest_namespace_replace_count"` 18 | Trigger struct { 19 | Type string `json:"type,omitempty"` 20 | TriggerSettings struct { 21 | Cron string `json:"cron,omitempty"` 22 | } `json:"trigger_settings,omitempty"` 23 | } `json:"trigger,omitempty"` 24 | Enabled bool `json:"enabled"` 25 | Deletion bool `json:"deletion,omitempty"` 26 | Override bool `json:"override,omitempty"` 27 | CopyByChunk bool `json:"copy_by_chunk,omitempty"` 28 | Filters []ReplicationFilters `json:"filters,omitempty"` 29 | Speed int `json:"speed,omitempty"` 30 | } 31 | 32 | type ReplicationFilters struct { 33 | Type string `json:"type,omitempty"` 34 | Value interface{} `json:"value,omitempty"` 35 | Decoration string `json:"decoration,omitempty"` 36 | } 37 | 38 | type ExecutionBody struct { 39 | PolicyID int `json:"policy_id"` 40 | } 41 | -------------------------------------------------------------------------------- /models/repositories.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type RepositoryBody struct { 4 | ArtifactCount int `json:"artifact_count,omitempty"` 5 | ID int `json:"id,omitempty"` 6 | ProjectID int `json:"project_id,omitempty"` 7 | PullCount int `json:"pull_count,omitempty"` 8 | Name string `json:"name,omitempty"` 9 | CreationTime string `json:"creation_time,omitempty"` 10 | UpdateTime string `json:"update_time,omitempty"` 11 | } 12 | -------------------------------------------------------------------------------- /models/retention.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | var PathRetentions = "/retentions" 4 | 5 | type Retention struct { 6 | Id int `json:"id,omitempty"` 7 | Algorithm string `json:"algorithm"` 8 | Rules []Rules `json:"rules"` 9 | Trigger Trigger `json:"trigger"` 10 | Scope Scope `json:"scope"` 11 | } 12 | type Repository struct { 13 | Kind string `json:"kind"` 14 | Decoration string `json:"decoration"` 15 | Pattern string `json:"pattern"` 16 | } 17 | type ScopeSelectors struct { 18 | Repository []Repository `json:"repository"` 19 | } 20 | type TagSelectors struct { 21 | Kind string `json:"kind"` 22 | Decoration string `json:"decoration"` 23 | Pattern string `json:"pattern"` 24 | Extras string `json:"extras"` 25 | } 26 | type Params struct { 27 | LatestPushedK int `json:"latestPushedK,omitempty"` 28 | LatestPulledN int `json:"latestPulledN,omitempty"` 29 | NDaysSinceLastPush int `json:"nDaysSinceLastPush,omitempty"` 30 | NDaysSinceLastPull int `json:"nDaysSinceLastPull,omitempty"` 31 | } 32 | type Rules struct { 33 | Disabled bool `json:"disabled"` 34 | Action string `json:"action"` 35 | ScopeSelectors ScopeSelectors `json:"scope_selectors"` 36 | TagSelectors []TagSelectors `json:"tag_selectors"` 37 | Params Params `json:"params"` 38 | Template string `json:"template"` 39 | } 40 | type References struct { 41 | } 42 | type Settings struct { 43 | Cron string `json:"cron"` 44 | } 45 | type Trigger struct { 46 | Kind string `json:"kind"` 47 | References References `json:"references"` 48 | Settings Settings `json:"settings"` 49 | } 50 | type Scope struct { 51 | Level string `json:"level"` 52 | Ref int `json:"ref"` 53 | } 54 | -------------------------------------------------------------------------------- /models/robot_account.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | var PathRobots = "/robots" 4 | 5 | type RobotBodyPermission struct { 6 | Access []RobotBodyAccess `json:"access,omitempty"` 7 | Kind string `json:"kind,omitempty"` 8 | Namespace string `json:"namespace,omitempty"` 9 | } 10 | type RobotBodyAccess struct { 11 | Action string `json:"action,omitempty"` 12 | Resource string `json:"resource,omitempty"` 13 | Effect string `json:"effect,omitempty"` 14 | } 15 | type RobotBody struct { 16 | ID int `json:"id,omitempty"` 17 | Name string `json:"name,omitempty"` 18 | Level string `json:"level,omitempty"` 19 | Description string `json:"description,omitempty"` 20 | Secret string `json:"secret,omitempty"` 21 | Duration int `json:"duration,omitempty"` 22 | Disable bool `json:"disable,omitempty"` 23 | Permissions []RobotBodyPermission `json:"permissions,omitempty"` 24 | } 25 | type RobotBodyResponse struct { 26 | ID int `json:"id,omitempty"` 27 | Name string `json:"name,omitempty"` 28 | Secret string `json:"secret,omitempty"` 29 | ExpiresAt int `json:"expires_at,omitempty"` 30 | CreationTime string `json:"creation_time,omitempty"` 31 | } 32 | 33 | type RobotSecret struct { 34 | Secret string `json:"secret"` 35 | } 36 | -------------------------------------------------------------------------------- /models/system.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "time" 4 | 5 | var PathVuln = "/system/scanAll/schedule" 6 | var PathScanners = "/scanners" 7 | 8 | var PathGC = "/system/gc/schedule" 9 | var PathPurgeAudit = "/system/purgeaudit/schedule" 10 | 11 | type JobParameters struct { 12 | DeleteUntagged bool `json:"delete_untagged,omitempty"` 13 | AuditRetentionHour int `json:"audit_retention_hour,omitempty"` 14 | IncludeOperations string `json:"include_operations,omitempty"` 15 | Workers int `json:"workers,omitempty"` 16 | } 17 | 18 | type SystemBody struct { 19 | Schedule struct { 20 | Type string `json:"type,omitempty"` 21 | Cron string `json:"cron,omitempty"` 22 | } `json:"schedule,omitempty"` 23 | ID int `json:"id,omitempty"` 24 | JobName string `json:"job_name,omitempty"` 25 | JobKind string `json:"job_kind,omitempty"` 26 | JobParameters string `json:"job_parameters,omitempty"` 27 | JobStatus string `json:"job_status,omitempty"` 28 | Deleted bool `json:"deleted,omitempty"` 29 | CreationTime time.Time `json:"creation_time,omitempty"` 30 | UpdateTime time.Time `json:"update_time,omitempty"` 31 | Parameters struct { 32 | DeleteUntagged bool `json:"delete_untagged,omitempty"` 33 | AuditRetentionHour int `json:"audit_retention_hour,omitempty"` 34 | IncludeOperations string `json:"include_operations,omitempty"` 35 | Workers int `json:"workers,omitempty"` 36 | AdditionalProp1 bool `json:"additionalProp1,omitempty"` 37 | AdditionalProp2 bool `json:"additionalProp2,omitempty"` 38 | AdditionalProp3 bool `json:"additionalProp3,omitempty"` 39 | } `json:"parameters"` 40 | } 41 | 42 | type ScannerBody struct { 43 | UUID string `json:"uuid,omitempty"` 44 | Name string `json:"name,omitempty"` 45 | Description string `json:"description,omitempty"` 46 | URL string `json:"url,omitempty"` 47 | Disabled bool `json:"disabled,omitempty"` 48 | IsDefault bool `json:"is_default,omitempty"` 49 | Auth string `json:"auth,omitempty"` 50 | SkipCertVerify bool `json:"skip_certVerify,omitempty"` 51 | UseInternalAddr bool `json:"use_internal_addr,omitempty"` 52 | CreateTime time.Time `json:"create_time,omitempty"` 53 | UpdateTime time.Time `json:"update_time,omitempty"` 54 | } 55 | -------------------------------------------------------------------------------- /models/systemcve.go: -------------------------------------------------------------------------------- 1 | package models 2 | var PathSystemCVEAllowList = "/system/CVEAllowlist" 3 | type SystemCveAllowListBodyPost struct { 4 | ID int `json:"id,omitempty"` 5 | Items SystemCveAllowlistItems `json:"items,omitempty"` 6 | UpdateTime string `json:"update_time,omitempty"` 7 | CreationTime string `json:"creation_time,omitempty"` 8 | ExpiresAt int `json:"expires_at,omitempty"` 9 | } 10 | 11 | type SystemCveAllowlistItems []struct { 12 | CveID string `json:"cve_id,omitempty"` 13 | } 14 | 15 | type SystemCveAllowlistItem struct { 16 | CveID string `json:"cve_id,omitempty"` 17 | } 18 | -------------------------------------------------------------------------------- /models/user.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | var PathUsers = "/users" 4 | 5 | // 6 | type UserBody struct { 7 | Location string `json:"location,omitempty"` 8 | Username string `json:"username,omitempty"` 9 | Comment string `json:"comment,omitempty"` 10 | UpdateTime string `json:"update_time,omitempty"` 11 | Password string `json:"password,omitempty"` 12 | UserID int `json:"user_id,omitempty"` 13 | Realname string `json:"realname,omitempty"` 14 | Deleted bool `json:"deleted,omitempty"` 15 | CreationTime string `json:"creation_time,omitempty"` 16 | AdminRoleInAuth bool `json:"admin_role_in_auth,omitempty"` 17 | RoleID int `json:"role_id,omitempty"` 18 | SysadminFlag bool `json:"sysadmin_flag,omitempty"` 19 | RoleName string `json:"role_name,omitempty"` 20 | ResetUUID string `json:"reset_uuid,omitempty"` 21 | Salt string `json:"Salt,omitempty"` 22 | Email string `json:"email,omitempty"` 23 | Newpassword string `json:"new_password,omitempty"` 24 | } 25 | -------------------------------------------------------------------------------- /provider/data_groups.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strconv" 7 | 8 | "github.com/goharbor/terraform-provider-harbor/client" 9 | "github.com/goharbor/terraform-provider-harbor/models" 10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 11 | ) 12 | 13 | func dataGroups() *schema.Resource { 14 | return &schema.Resource{ 15 | Read: dataGroupsRead, 16 | Schema: map[string]*schema.Schema{ 17 | "group_name": { 18 | Type: schema.TypeString, 19 | Optional: true, 20 | Default: "", 21 | }, 22 | "ldap_group_dn": { 23 | Type: schema.TypeString, 24 | Optional: true, 25 | Default: "", 26 | }, 27 | "groups": { 28 | Type: schema.TypeList, 29 | Computed: true, 30 | Elem: &schema.Resource{ 31 | Schema: map[string]*schema.Schema{ 32 | "group_name": { 33 | Type: schema.TypeString, 34 | Computed: true, 35 | }, 36 | "group_type": { 37 | Type: schema.TypeInt, 38 | Computed: true, 39 | }, 40 | "id": { 41 | Type: schema.TypeInt, 42 | Computed: true, 43 | }, 44 | "ldap_group_dn": { 45 | Type: schema.TypeString, 46 | Computed: true, 47 | }, 48 | }, 49 | }, 50 | }, 51 | }, 52 | } 53 | } 54 | 55 | func dataGroupsRead(d *schema.ResourceData, m interface{}) error { 56 | apiClient := m.(*client.Client) 57 | 58 | groupName := d.Get("group_name").(string) 59 | ldapGroupDN := d.Get("ldap_group_dn").(string) 60 | 61 | page := 1 62 | userGroupsData := make([]map[string]interface{}, 0) 63 | for { 64 | resp, _, _, err := apiClient.SendRequest("GET", models.PathGroups+"?page="+strconv.Itoa(page), nil, 200) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | var jsonData []models.GroupBody 70 | err = json.Unmarshal([]byte(resp), &jsonData) 71 | if err != nil { 72 | return fmt.Errorf("unable to retrieve Harbor user groups data: %s", err) 73 | } 74 | 75 | // If there is no data on the current page, we have reached the last page 76 | if len(jsonData) == 0 { 77 | break 78 | } 79 | 80 | for _, v := range jsonData { 81 | if (groupName == "" || v.Groupname == groupName) && 82 | (ldapGroupDN == "" || v.LdapGroupDn == ldapGroupDN) { 83 | 84 | userGroupData := map[string]interface{}{ 85 | "group_name": v.Groupname, 86 | "group_type": v.GroupType, 87 | "id": v.ID, 88 | "ldap_group_dn": v.LdapGroupDn, 89 | } 90 | 91 | userGroupsData = append(userGroupsData, userGroupData) 92 | } 93 | } 94 | 95 | page++ 96 | } 97 | d.SetId("harbor-user-groups") 98 | d.Set("groups", userGroupsData) 99 | 100 | return nil 101 | } 102 | -------------------------------------------------------------------------------- /provider/data_project.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strconv" 7 | 8 | "github.com/goharbor/terraform-provider-harbor/client" 9 | "github.com/goharbor/terraform-provider-harbor/models" 10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 11 | ) 12 | 13 | func dataProject() *schema.Resource { 14 | return &schema.Resource{ 15 | Read: dataProjectRead, 16 | Schema: map[string]*schema.Schema{ 17 | "name": { 18 | Type: schema.TypeString, 19 | Required: true, 20 | }, 21 | "type": { 22 | Type: schema.TypeString, 23 | Computed: true, 24 | }, 25 | "project_id": { 26 | Type: schema.TypeInt, 27 | Computed: true, 28 | }, 29 | "public": { 30 | Type: schema.TypeBool, 31 | Computed: true, 32 | }, 33 | "vulnerability_scanning": { 34 | Type: schema.TypeBool, 35 | Computed: true, 36 | }, 37 | }, 38 | } 39 | } 40 | 41 | func dataProjectRead(d *schema.ResourceData, m interface{}) error { 42 | apiClient := m.(*client.Client) 43 | name := d.Get("name").(string) 44 | projectPath := models.PathProjects + "?name=" + name 45 | 46 | resp, _, _, err := apiClient.SendRequest("GET", projectPath, nil, 200) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | var jsonData []models.ProjectsBodyResponses 52 | err = json.Unmarshal([]byte(resp), &jsonData) 53 | if err != nil { 54 | return fmt.Errorf("unable to find the project named: %s", name) 55 | } 56 | 57 | for _, v := range jsonData { 58 | 59 | if v.Name == name { 60 | id := models.PathProjects + "/" + strconv.Itoa(v.ProjectID) 61 | public := getboolfromstring(v.Metadata.Public) 62 | autoScan := getboolfromstring(v.Metadata.AutoScan) 63 | project_type := getProjectType(v) 64 | 65 | d.SetId(id) 66 | d.Set("project_id", v.ProjectID) 67 | d.Set("name", v.Name) 68 | d.Set("public", public) 69 | d.Set("vulnerability_scanning", autoScan) 70 | d.Set("type", project_type) 71 | } 72 | } 73 | return nil 74 | } 75 | 76 | func getProjectType(project models.ProjectsBodyResponses) string { 77 | if project.RegistryID != nil { 78 | return "ProxyCache" 79 | } 80 | return "Project" 81 | } 82 | 83 | func getboolfromstring(stringbool string) bool { 84 | boolbool, err := strconv.ParseBool(stringbool) 85 | if err != nil { 86 | return false 87 | } 88 | return boolbool 89 | } 90 | -------------------------------------------------------------------------------- /provider/data_project_member_groups.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strconv" 7 | 8 | "github.com/goharbor/terraform-provider-harbor/client" 9 | "github.com/goharbor/terraform-provider-harbor/models" 10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 11 | ) 12 | 13 | func dataProjectMemberGroups() *schema.Resource { 14 | return &schema.Resource{ 15 | Read: dataProjectMemberGroupsRead, 16 | Schema: map[string]*schema.Schema{ 17 | "project_id": { 18 | Type: schema.TypeString, 19 | Required: true, 20 | }, 21 | "project_member_groups": { 22 | Type: schema.TypeList, 23 | Computed: true, 24 | Elem: &schema.Resource{ 25 | Schema: map[string]*schema.Schema{ 26 | "id": { 27 | Type: schema.TypeString, 28 | Computed: true, 29 | }, 30 | "project_id": { 31 | Type: schema.TypeString, 32 | Computed: true, 33 | }, 34 | "group_name": { 35 | Type: schema.TypeString, 36 | Computed: true, 37 | }, 38 | "role": { 39 | Type: schema.TypeString, 40 | Computed: true, 41 | }, 42 | }, 43 | }, 44 | }, 45 | }, 46 | } 47 | } 48 | 49 | func dataProjectMemberGroupsRead(d *schema.ResourceData, m interface{}) error { 50 | apiClient := m.(*client.Client) 51 | 52 | projectId := checkProjectid(d.Get("project_id").(string)) 53 | path := projectId + "/members" 54 | 55 | page := 1 56 | projectMemberGroupsData := make([]map[string]interface{}, 0) 57 | for { 58 | resp, _, _, err := apiClient.SendRequest("GET", path+"?page="+strconv.Itoa(page), nil, 200) 59 | if err != nil { 60 | return err 61 | } 62 | 63 | var jsonData []models.ProjectMembersBodyResponses 64 | err = json.Unmarshal([]byte(resp), &jsonData) 65 | if err != nil { 66 | return fmt.Errorf("unable to retrieve Harbor project member groups data: %s", err) 67 | } 68 | 69 | // If there is no data on the current page, we have reached the last page 70 | if len(jsonData) == 0 { 71 | break 72 | } 73 | 74 | for _, v := range jsonData { 75 | if v.EntityType == "g" { 76 | 77 | projectMemberGroupData := map[string]interface{}{ 78 | "id": path + "/" + strconv.Itoa(v.ID), 79 | "project_id": checkProjectid(strconv.Itoa(v.ProjectID)), 80 | "group_name": v.EntityName, 81 | "role": client.RoleTypeNumber(v.RoleID), 82 | } 83 | 84 | projectMemberGroupsData = append(projectMemberGroupsData, projectMemberGroupData) 85 | } 86 | } 87 | 88 | page++ 89 | } 90 | d.SetId("harbor-project-member-groups") 91 | d.Set("project_member_groups", projectMemberGroupsData) 92 | 93 | return nil 94 | } 95 | -------------------------------------------------------------------------------- /provider/data_project_member_users.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strconv" 7 | 8 | "github.com/goharbor/terraform-provider-harbor/client" 9 | "github.com/goharbor/terraform-provider-harbor/models" 10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 11 | ) 12 | 13 | func dataProjectMemberUsers() *schema.Resource { 14 | return &schema.Resource{ 15 | Read: dataProjectMemberUsersRead, 16 | Schema: map[string]*schema.Schema{ 17 | "project_id": { 18 | Type: schema.TypeString, 19 | Required: true, 20 | }, 21 | "project_member_users": { 22 | Type: schema.TypeList, 23 | Computed: true, 24 | Elem: &schema.Resource{ 25 | Schema: map[string]*schema.Schema{ 26 | "id": { 27 | Type: schema.TypeString, 28 | Computed: true, 29 | }, 30 | "project_id": { 31 | Type: schema.TypeString, 32 | Computed: true, 33 | }, 34 | "user_name": { 35 | Type: schema.TypeString, 36 | Computed: true, 37 | }, 38 | "role": { 39 | Type: schema.TypeString, 40 | Computed: true, 41 | }, 42 | }, 43 | }, 44 | }, 45 | }, 46 | } 47 | } 48 | 49 | func dataProjectMemberUsersRead(d *schema.ResourceData, m interface{}) error { 50 | apiClient := m.(*client.Client) 51 | 52 | projectId := checkProjectid(d.Get("project_id").(string)) 53 | path := projectId + "/members" 54 | 55 | page := 1 56 | projectMemberUsersData := make([]map[string]interface{}, 0) 57 | for { 58 | resp, _, _, err := apiClient.SendRequest("GET", path+"?page="+strconv.Itoa(page), nil, 200) 59 | if err != nil { 60 | return err 61 | } 62 | 63 | var jsonData []models.ProjectMembersBodyResponses 64 | err = json.Unmarshal([]byte(resp), &jsonData) 65 | if err != nil { 66 | return fmt.Errorf("unable to retrieve Harbor project member users data: %s", err) 67 | } 68 | 69 | // If there is no data on the current page, we have reached the last page 70 | if len(jsonData) == 0 { 71 | break 72 | } 73 | 74 | for _, v := range jsonData { 75 | if v.EntityType == "u" { 76 | 77 | projectMemberUserData := map[string]interface{}{ 78 | "id": path + "/" + strconv.Itoa(v.ID), 79 | "project_id": checkProjectid(strconv.Itoa(v.ProjectID)), 80 | "user_name": v.EntityName, 81 | "role": client.RoleTypeNumber(v.RoleID), 82 | } 83 | 84 | projectMemberUsersData = append(projectMemberUsersData, projectMemberUserData) 85 | } 86 | } 87 | 88 | page++ 89 | } 90 | d.SetId("harbor-project-member-users") 91 | d.Set("project_member_users", projectMemberUsersData) 92 | 93 | return nil 94 | } 95 | -------------------------------------------------------------------------------- /provider/data_registry.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | "strconv" 8 | 9 | "github.com/goharbor/terraform-provider-harbor/client" 10 | "github.com/goharbor/terraform-provider-harbor/models" 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 12 | ) 13 | 14 | func dataRegistry() *schema.Resource { 15 | return &schema.Resource{ 16 | Read: dataRegistryRead, 17 | Schema: map[string]*schema.Schema{ 18 | "name": { 19 | Type: schema.TypeString, 20 | Required: true, 21 | }, 22 | "registry_id": { 23 | Type: schema.TypeInt, 24 | Computed: true, 25 | }, 26 | "description": { 27 | Type: schema.TypeString, 28 | Computed: true, 29 | }, 30 | "type": { 31 | Type: schema.TypeString, 32 | Computed: true, 33 | }, 34 | "url": { 35 | Type: schema.TypeString, 36 | Computed: true, 37 | }, 38 | "insecure": { 39 | Type: schema.TypeBool, 40 | Computed: true, 41 | }, 42 | "status": { 43 | Type: schema.TypeString, 44 | Computed: true, 45 | }, 46 | }, 47 | } 48 | } 49 | 50 | func dataRegistryRead(d *schema.ResourceData, m interface{}) error { 51 | apiClient := m.(*client.Client) 52 | name := d.Get("name").(string) 53 | registryPath := models.PathRegistries + "?name=" + url.QueryEscape(name) 54 | resp, _, _, err := apiClient.SendRequest("GET", registryPath, nil, 200) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | var jsonData []models.RegistryBody 60 | err = json.Unmarshal([]byte(resp), &jsonData) 61 | if err != nil { 62 | return fmt.Errorf("Unable to find the registry named: %s", name) 63 | } 64 | 65 | for _, v := range jsonData { 66 | if v.Name == name { 67 | id := models.PathProjects + "/" + strconv.Itoa(v.ID) 68 | 69 | d.SetId(id) 70 | d.Set("registry_id", v.ID) 71 | d.Set("name", v.Name) 72 | d.Set("type", v.Type) 73 | d.Set("description", v.Description) 74 | d.Set("url", v.URL) 75 | d.Set("insecure", v.Insecure) 76 | d.Set("status", v.Status) 77 | } 78 | } 79 | 80 | return nil 81 | } 82 | -------------------------------------------------------------------------------- /provider/data_registry_test.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 8 | ) 9 | 10 | func TestAccDataSourceRegistry_basic(t *testing.T) { 11 | for _, rName := range []string{"Foo", "Foo bar"} { 12 | t.Run(rName, func(t *testing.T) { 13 | resource.Test(t, resource.TestCase{ 14 | PreCheck: func() { testAccPreCheck(t) }, 15 | Providers: testAccProviders, 16 | Steps: []resource.TestStep{ 17 | { 18 | Config: testAccDataSourceRegistryConfig_basicDataSource(rName), 19 | Check: resource.ComposeAggregateTestCheckFunc( 20 | resource.TestCheckResourceAttr("data.harbor_registry.test", "name", rName), 21 | resource.TestCheckResourceAttr("data.harbor_registry.test", "type", "docker-hub"), 22 | resource.TestCheckResourceAttr("data.harbor_registry.test", "url", "https://hub.docker.com"), 23 | ), 24 | }, 25 | }, 26 | }) 27 | }) 28 | } 29 | } 30 | 31 | func testAccDataSourceRegistryConfig_basicDataSource(rName string) string { 32 | return fmt.Sprintf(` 33 | resource "harbor_registry" "test" { 34 | provider_name = "docker-hub" 35 | name = "%s" 36 | endpoint_url = "https://hub.docker.com" 37 | } 38 | 39 | data "harbor_registry" "test" { 40 | name = harbor_registry.test.name 41 | } 42 | `, rName) 43 | } 44 | -------------------------------------------------------------------------------- /provider/data_users.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/goharbor/terraform-provider-harbor/client" 10 | "github.com/goharbor/terraform-provider-harbor/models" 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 12 | ) 13 | 14 | func dataUsers() *schema.Resource { 15 | return &schema.Resource{ 16 | Read: dataUsersRead, 17 | Schema: map[string]*schema.Schema{ 18 | "username": { 19 | Type: schema.TypeString, 20 | Optional: true, 21 | Default: "", 22 | }, 23 | "email": { 24 | Type: schema.TypeString, 25 | Optional: true, 26 | Default: "", 27 | }, 28 | "users": { 29 | Type: schema.TypeList, 30 | Computed: true, 31 | Elem: &schema.Resource{ 32 | Schema: map[string]*schema.Schema{ 33 | "id": { 34 | Type: schema.TypeString, 35 | Computed: true, 36 | }, 37 | "username": { 38 | Type: schema.TypeString, 39 | Computed: true, 40 | }, 41 | "full_name": { 42 | Type: schema.TypeString, 43 | Computed: true, 44 | }, 45 | "email": { 46 | Type: schema.TypeString, 47 | Computed: true, 48 | }, 49 | "admin": { 50 | Type: schema.TypeBool, 51 | Computed: true, 52 | }, 53 | "comment": { 54 | Type: schema.TypeString, 55 | Computed: true, 56 | }, 57 | }, 58 | }, 59 | }, 60 | }, 61 | } 62 | } 63 | 64 | func dataUsersRead(d *schema.ResourceData, m interface{}) error { 65 | apiClient := m.(*client.Client) 66 | 67 | username := d.Get("username").(string) 68 | email := d.Get("email").(string) 69 | 70 | usersQueryPath := []string{} 71 | if username != "" { 72 | usersQueryPath = append(usersQueryPath, "username="+username) 73 | } 74 | if email != "" { 75 | usersQueryPath = append(usersQueryPath, "email="+email) 76 | } 77 | 78 | page := 1 79 | usersData := make([]map[string]interface{}, 0) 80 | for { 81 | usersPath := models.PathUsers + "?page=" + strconv.Itoa(page) 82 | if len(usersQueryPath) > 0 { 83 | usersPath += "&q=" + strings.Join(usersQueryPath, ",") 84 | } 85 | 86 | resp, _, _, err := apiClient.SendRequest("GET", usersPath, nil, 200) 87 | if err != nil { 88 | return err 89 | } 90 | 91 | var jsonData []models.UserBody 92 | err = json.Unmarshal([]byte(resp), &jsonData) 93 | if err != nil { 94 | return fmt.Errorf("unable to retrieve Harbor users data: %s", err) 95 | } 96 | 97 | // If there is no data on the current page, we have reached the last page 98 | if len(jsonData) == 0 { 99 | break 100 | } 101 | 102 | for _, v := range jsonData { 103 | id := models.PathUsers + "/" + strconv.Itoa(v.UserID) 104 | 105 | userData := map[string]interface{}{ 106 | "id": id, 107 | "username": v.Username, 108 | "full_name": v.Realname, 109 | "email": v.Email, 110 | "admin": v.SysadminFlag, 111 | "comment": v.Comment, 112 | } 113 | 114 | usersData = append(usersData, userData) 115 | } 116 | 117 | page++ 118 | } 119 | d.SetId("harbor-users") 120 | d.Set("users", usersData) 121 | 122 | return nil 123 | } 124 | -------------------------------------------------------------------------------- /provider/provider_test.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/goharbor/terraform-provider-harbor/client" 10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 13 | ) 14 | 15 | var testAccProviders map[string]*schema.Provider 16 | var testAccProvider *schema.Provider 17 | 18 | func init() { 19 | testAccProvider = Provider() 20 | testAccProviders = map[string]*schema.Provider{ 21 | "harbor": testAccProvider, 22 | } 23 | } 24 | 25 | func testProvider(t *testing.T) { 26 | if err := Provider().InternalValidate(); err != nil { 27 | t.Fatalf("err: %s", err) 28 | } 29 | } 30 | 31 | func testAccPreCheck(t *testing.T) { 32 | 33 | if v := os.Getenv("HARBOR_URL"); v == "" { 34 | t.Fatal("HARBOR_URL must be set for acceptance tests") 35 | } 36 | if v := os.Getenv("HARBOR_USERNAME"); v == "" { 37 | t.Fatal("HARBOR_USERNAME must be set for acceptance tests") 38 | } 39 | if v := os.Getenv("HARBOR_PASSWORD"); v == "" { 40 | t.Fatal("HARBOR_PASSWORD must be set for acceptance tests") 41 | } 42 | 43 | } 44 | 45 | func testAccCheckResourceExists(resource string) resource.TestCheckFunc { 46 | 47 | return func(state *terraform.State) error { 48 | rs, ok := state.RootModule().Resources[resource] 49 | if !ok { 50 | return fmt.Errorf("Not found: %s", resource) 51 | } 52 | if rs.Primary.ID == "" { 53 | return fmt.Errorf("No Record ID is set") 54 | } 55 | name := rs.Primary.ID 56 | 57 | if strings.HasPrefix(name, "configuration/") { 58 | name = "/configurations" 59 | } 60 | 61 | apiClient := testAccProvider.Meta().(*client.Client) 62 | _, _, _, err := apiClient.SendRequest("GET", name, nil, 200) 63 | if err != nil { 64 | return fmt.Errorf("error fetching item with resource %s. %s", resource, err) 65 | } 66 | return nil 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /provider/random.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import "math/rand" 4 | 5 | func randomString(n int) string { 6 | var letter = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") 7 | 8 | b := make([]rune, n) 9 | for i := range b { 10 | b[i] = letter[rand.Intn(len(letter))] 11 | } 12 | return string(b) 13 | } 14 | -------------------------------------------------------------------------------- /provider/random_test.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestRandomString(t *testing.T) { 8 | result := randomString(15) 9 | length := len(result) 10 | 11 | if length != 15 { 12 | t.Error("Failed not the correct length") 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /provider/resource_config_security.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/goharbor/terraform-provider-harbor/client" 8 | "github.com/goharbor/terraform-provider-harbor/models" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | ) 11 | 12 | func resourceConfigSecurity() *schema.Resource { 13 | return &schema.Resource{ 14 | Schema: map[string]*schema.Schema{ 15 | "cve_allowlist": { 16 | Type: schema.TypeList, 17 | Elem: &schema.Schema{ 18 | Type: schema.TypeString, 19 | }, 20 | Required: true, 21 | }, 22 | "expires_at": { 23 | Type: schema.TypeInt, 24 | Optional: true, 25 | }, 26 | "update_time": { 27 | Type: schema.TypeString, 28 | Computed: true, 29 | }, 30 | "creation_time": { 31 | Type: schema.TypeString, 32 | Computed: true, 33 | }, 34 | }, 35 | Create: resourceConfigSecurityCreate, 36 | Read: resourceConfigSecurityRead, 37 | Update: resourceConfigSecurityUpdate, 38 | Delete: resourceConfigSecurityDelete, 39 | Importer: &schema.ResourceImporter{ 40 | StateContext: schema.ImportStatePassthroughContext, 41 | }, 42 | } 43 | } 44 | 45 | func resourceConfigSecurityCreate(d *schema.ResourceData, m interface{}) error { 46 | apiClient := m.(*client.Client) 47 | body := client.SystemCVEAllowListBody(d) 48 | 49 | _, _, _, err := apiClient.SendRequest("PUT", models.PathSystemCVEAllowList, body, 200) 50 | if err != nil { 51 | return err 52 | } 53 | 54 | return resourceConfigSecurityRead(d, m) 55 | } 56 | 57 | func resourceConfigSecurityRead(d *schema.ResourceData, m interface{}) error { 58 | apiClient := m.(*client.Client) 59 | 60 | resp, _, respCode, err := apiClient.SendRequest("GET", models.PathSystemCVEAllowList, nil, 200) 61 | if respCode == 404 && err != nil { 62 | d.SetId("") 63 | return fmt.Errorf("resource not found %s", models.PathSystemCVEAllowList) 64 | } 65 | 66 | var jsonData models.SystemCveAllowListBodyPost 67 | err = json.Unmarshal([]byte(resp), &jsonData) 68 | if err != nil { 69 | return fmt.Errorf("resource not found %s", models.PathSystemCVEAllowList) 70 | } 71 | 72 | // Convert the list of SystemCveAllowlistItems to a list of strings 73 | allowlistItems := make([]string, len(jsonData.Items)) 74 | for i, item := range jsonData.Items { 75 | allowlistItems[i] = item.CveID 76 | } 77 | 78 | d.SetId(models.PathSystemCVEAllowList) 79 | d.Set("update_time", jsonData.UpdateTime) 80 | d.Set("expires_at", jsonData.ExpiresAt) 81 | d.Set("cve_allowlist", allowlistItems) 82 | d.Set("creation_time", jsonData.CreationTime) 83 | 84 | return nil 85 | } 86 | 87 | func resourceConfigSecurityUpdate(d *schema.ResourceData, m interface{}) error { 88 | return resourceConfigSecurityCreate(d, m) 89 | } 90 | 91 | func resourceConfigSecurityDelete(d *schema.ResourceData, m interface{}) error { 92 | d.Set("expires_at", nil) 93 | d.Set("cve_allowlist", []string{}) 94 | return resourceConfigSecurityCreate(d, m) 95 | } 96 | -------------------------------------------------------------------------------- /provider/resource_config_system_test.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/goharbor/terraform-provider-harbor/client" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 10 | ) 11 | 12 | const resourceConfigSystemMain = "harbor_config_system.main" 13 | 14 | func testAccCheckConfigSystemDestroy(s *terraform.State) error { 15 | apiClient := testAccProvider.Meta().(*client.Client) 16 | r := "harbor_config_system" 17 | 18 | for _, rs := range s.RootModule().Resources { 19 | if rs.Type != r { 20 | continue 21 | } 22 | 23 | resp, _, _, err := apiClient.SendRequest("GET", "/configurations", nil, 200) 24 | if err != nil { 25 | return fmt.Errorf("Resource was not deleted\n%s", resp) 26 | } 27 | } 28 | 29 | return nil 30 | } 31 | 32 | func TestAccConfigSystem(t *testing.T) { 33 | resource.Test(t, resource.TestCase{ 34 | PreCheck: func() { testAccPreCheck(t) }, 35 | Providers: testAccProviders, 36 | CheckDestroy: testAccCheckConfigSystemDestroy, 37 | Steps: []resource.TestStep{ 38 | { 39 | Config: testAccCheckConfigSystem(), 40 | Check: resource.ComposeTestCheckFunc( 41 | testAccCheckResourceExists(resourceConfigSystemMain), 42 | resource.TestCheckResourceAttr( 43 | resourceConfigSystemMain, "project_creation_restriction", "adminonly"), 44 | resource.TestCheckResourceAttr( 45 | resourceConfigSystemMain, "read_only", "false"), 46 | resource.TestCheckResourceAttr( 47 | resourceConfigSystemMain, "robot_token_expiration", "30"), 48 | resource.TestCheckResourceAttr( 49 | resourceConfigSystemMain, "robot_name_prefix", "robot$"), 50 | resource.TestCheckResourceAttr( 51 | resourceConfigSystemMain, "scanner_skip_update_pulltime", "false"), 52 | ), 53 | }, 54 | }, 55 | }) 56 | } 57 | 58 | func testAccCheckConfigSystem() string { 59 | return fmt.Sprintf(` 60 | resource "harbor_config_system" "main" { 61 | project_creation_restriction = "adminonly" 62 | read_only = false 63 | robot_token_expiration = 30 64 | robot_name_prefix = "robot$" 65 | scanner_skip_update_pulltime = false 66 | } 67 | `) 68 | } 69 | -------------------------------------------------------------------------------- /provider/resource_garbage_collection.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/goharbor/terraform-provider-harbor/client" 8 | "github.com/goharbor/terraform-provider-harbor/models" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | ) 11 | 12 | func resourceGC() *schema.Resource { 13 | return &schema.Resource{ 14 | Schema: map[string]*schema.Schema{ 15 | "schedule": { 16 | Type: schema.TypeString, 17 | Required: true, 18 | }, 19 | "delete_untagged": { 20 | Type: schema.TypeBool, 21 | Optional: true, 22 | }, 23 | "workers": { 24 | Type: schema.TypeInt, 25 | Optional: true, 26 | Default: 1, 27 | ValidateFunc: func(i interface{}, k string) (ws []string, errors []error) { 28 | value := i.(int) 29 | if value < 1 || value > 5 { 30 | errors = append(errors, fmt.Errorf("GC workers must be between 1 and 5")) 31 | } 32 | return 33 | }, 34 | }, 35 | }, 36 | Create: resourceGCCreate, 37 | Read: resourceGCRead, 38 | Update: resourceGCCreate, 39 | Delete: resourceGCDelete, 40 | } 41 | } 42 | 43 | func resourceGCCreate(d *schema.ResourceData, m interface{}) error { 44 | apiClient := m.(*client.Client) 45 | 46 | err := apiClient.SetSchedule(d, "gc") 47 | if err != nil { 48 | return err 49 | } 50 | 51 | d.SetId("/system/gc/schedule") 52 | return resourceGCRead(d, m) 53 | } 54 | 55 | func resourceGCRead(d *schema.ResourceData, m interface{}) error { 56 | apiClient := m.(*client.Client) 57 | 58 | resp, _, respCode, err := apiClient.SendRequest("GET", models.PathGC, nil, 200) 59 | if respCode == 404 && err != nil { 60 | d.SetId("") 61 | return fmt.Errorf("Resource not found %s", d.Id()) 62 | } 63 | 64 | var jsonData models.SystemBody 65 | err = json.Unmarshal([]byte(resp), &jsonData) 66 | if err != nil { 67 | return err 68 | } 69 | 70 | jobParameters := jsonData.JobParameters 71 | 72 | var jsonJobParameters models.JobParameters 73 | err = json.Unmarshal([]byte(jobParameters), &jsonJobParameters) 74 | if err != nil { 75 | fmt.Println(err) 76 | } 77 | 78 | if jsonData.Schedule.Type == "Custom" { 79 | d.Set("schedule", jsonData.Schedule.Cron) 80 | } else { 81 | d.Set("schedule", jsonData.Schedule.Type) 82 | } 83 | d.Set("delete_untagged", jsonJobParameters.DeleteUntagged) 84 | d.Set("workers", jsonJobParameters.Workers) 85 | return nil 86 | } 87 | 88 | func resourceGCDelete(d *schema.ResourceData, m interface{}) error { 89 | apiClient := m.(*client.Client) 90 | 91 | body := models.SystemBody{} 92 | body.Schedule.Cron = "" 93 | body.Schedule.Type = "None" 94 | body.Parameters.DeleteUntagged = false 95 | 96 | _, _, _, err := apiClient.SendRequest("PUT", models.PathGC, body, 200) 97 | if err != nil { 98 | return err 99 | } 100 | return nil 101 | } 102 | -------------------------------------------------------------------------------- /provider/resource_garbage_collection_test.go: -------------------------------------------------------------------------------- 1 | //go:build external_auth 2 | // +build external_auth 3 | 4 | package provider 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | 10 | "github.com/goharbor/terraform-provider-harbor/client" 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 13 | ) 14 | 15 | const harborGCMain = "harbor_garbage_collection.main" 16 | 17 | func testAccCheckGCDestroy(s *terraform.State) error { 18 | apiClient := testAccProvider.Meta().(*client.Client) 19 | 20 | for _, rs := range s.RootModule().Resources { 21 | if rs.Type != "harbor_garbage_collection" { 22 | continue 23 | } 24 | 25 | resp, _, _, err := apiClient.SendRequest("GET", rs.Primary.ID, nil, 200) 26 | if err != nil { 27 | return fmt.Errorf("Resouse was not delete \n %s", resp) 28 | } 29 | if resp != "" { 30 | return fmt.Errorf("Resouse was not delete \n %s", resp) 31 | } 32 | 33 | } 34 | 35 | return nil 36 | } 37 | 38 | func TestAccGCUpdate(t *testing.T) { 39 | resource.Test(t, resource.TestCase{ 40 | PreCheck: func() { testAccPreCheck(t) }, 41 | Providers: testAccProviders, 42 | CheckDestroy: testAccCheckGCDestroy, 43 | Steps: []resource.TestStep{ 44 | { 45 | Config: testAccCheckGCBasic(), 46 | Check: resource.ComposeTestCheckFunc( 47 | testAccCheckResourceExists(harborGCMain), 48 | resource.TestCheckResourceAttr( 49 | harborGCMain, "schedule", "Daily"), 50 | ), 51 | }, 52 | { 53 | Config: testAccCheckGCUpdate(), 54 | Check: resource.ComposeTestCheckFunc( 55 | testAccCheckResourceExists(harborGCMain), 56 | resource.TestCheckResourceAttr( 57 | harborGCMain, "schedule", "Hourly"), 58 | resource.TestCheckResourceAttr( 59 | harborGCMain, "delete_untagged", "true"), 60 | ), 61 | }, 62 | }, 63 | }) 64 | } 65 | 66 | func testAccCheckGCBasic() string { 67 | return fmt.Sprintf(` 68 | resource "harbor_garbage_collection" "main" { 69 | schedule = "Daily" 70 | } 71 | `) 72 | } 73 | 74 | func testAccCheckGCUpdate() string { 75 | return fmt.Sprintf(` 76 | resource "harbor_garbage_collection" "main" { 77 | schedule = "Hourly" 78 | delete_untagged = true 79 | workers = 2 80 | } 81 | `) 82 | } 83 | -------------------------------------------------------------------------------- /provider/resource_group.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/goharbor/terraform-provider-harbor/client" 8 | "github.com/goharbor/terraform-provider-harbor/models" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | ) 11 | 12 | func resourceGroup() *schema.Resource { 13 | return &schema.Resource{ 14 | Schema: map[string]*schema.Schema{ 15 | "group_name": { 16 | Type: schema.TypeString, 17 | Required: true, 18 | ForceNew: true, 19 | }, 20 | "group_type": { 21 | Type: schema.TypeInt, 22 | Required: true, 23 | }, 24 | "ldap_group_dn": { 25 | Type: schema.TypeString, 26 | Optional: true, 27 | }, 28 | }, 29 | Create: resourceGroupCreate, 30 | Read: resourceGroupRead, 31 | Update: resourceGroupUpdate, 32 | Delete: resourceGroupDelete, 33 | Importer: &schema.ResourceImporter{ 34 | State: schema.ImportStatePassthrough, 35 | }, 36 | } 37 | } 38 | 39 | func resourceGroupCreate(d *schema.ResourceData, m interface{}) error { 40 | apiClient := m.(*client.Client) 41 | 42 | body := client.GroupBody(d) 43 | 44 | _, header, _, err := apiClient.SendRequest("POST", models.PathGroups, &body, 201) 45 | if err != nil { 46 | return err 47 | } 48 | 49 | id, err := client.GetID(header) 50 | if err != nil { 51 | return nil 52 | } 53 | 54 | d.SetId(id) 55 | return resourceGroupRead(d, m) 56 | } 57 | 58 | func resourceGroupRead(d *schema.ResourceData, m interface{}) error { 59 | apiClient := m.(*client.Client) 60 | resp, _, respCode, err := apiClient.SendRequest("GET", d.Id(), nil, 200) 61 | if respCode == 404 && err != nil { 62 | d.SetId("") 63 | return nil 64 | } else if err != nil { 65 | return fmt.Errorf("resource not found %s", d.Id()) 66 | } 67 | 68 | var jsonData models.GroupBody 69 | err = json.Unmarshal([]byte(resp), &jsonData) 70 | if err != nil { 71 | return fmt.Errorf("Resource not found %s", d.Id()) 72 | } 73 | 74 | d.Set("group_name", jsonData.Groupname) 75 | d.Set("group_type", jsonData.GroupType) 76 | 77 | return nil 78 | } 79 | 80 | func resourceGroupUpdate(d *schema.ResourceData, m interface{}) error { 81 | return resourceGroupRead(d, m) 82 | } 83 | 84 | func resourceGroupDelete(d *schema.ResourceData, m interface{}) error { 85 | apiClient := m.(*client.Client) 86 | 87 | _, _, respCode, err := apiClient.SendRequest("DELETE", d.Id(), nil, 200) 88 | if respCode != 404 && err != nil { // We can't delete something that doesn't exist. Hence the 404-check 89 | return err 90 | } 91 | return nil 92 | } 93 | -------------------------------------------------------------------------------- /provider/resource_interrogation_services.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/goharbor/terraform-provider-harbor/client" 8 | "github.com/goharbor/terraform-provider-harbor/models" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | ) 11 | 12 | func resourceVuln() *schema.Resource { 13 | return &schema.Resource{ 14 | Schema: map[string]*schema.Schema{ 15 | "vulnerability_scan_policy": { 16 | Type: schema.TypeString, 17 | Required: true, 18 | }, 19 | "default_scanner": { 20 | Type: schema.TypeString, 21 | Optional: true, 22 | }, 23 | }, 24 | Create: resourceVulnCreate, 25 | Read: resourceVulnRead, 26 | Update: resourceVulnCreate, 27 | Delete: resourceVulnDelete, 28 | } 29 | } 30 | 31 | func resourceVulnCreate(d *schema.ResourceData, m interface{}) error { 32 | apiClient := m.(*client.Client) 33 | 34 | err := apiClient.SetSchedule(d, "vuln") 35 | if err != nil { 36 | return err 37 | } 38 | 39 | scanner := d.Get("default_scanner").(string) 40 | if scanner != "" { 41 | apiClient.SetDefaultScanner(scanner) 42 | } 43 | 44 | return resourceVulnRead(d, m) 45 | } 46 | 47 | func resourceVulnRead(d *schema.ResourceData, m interface{}) error { 48 | d.SetId("/system/scanAll/schedule") 49 | apiClient := m.(*client.Client) 50 | resp, _, respCode, err := apiClient.SendRequest("GET", d.Id(), nil, 200) 51 | 52 | if respCode == 404 && err != nil { 53 | d.SetId("") 54 | return fmt.Errorf("Resource not found %s", d.Id()) 55 | } 56 | 57 | var jsonData models.InterogationsBodyResponse 58 | err = json.Unmarshal([]byte(resp), &jsonData) 59 | if err != nil { 60 | return fmt.Errorf("Resource not found %s", d.Id()) 61 | } 62 | 63 | vulnerability_scan_policy := jsonData.Schedule.Type 64 | if vulnerability_scan_policy == "Custom" { 65 | vulnerability_scan_policy = jsonData.Schedule.Cron 66 | } 67 | 68 | d.Set("vulnerability_scan_policy", vulnerability_scan_policy) 69 | 70 | return nil 71 | } 72 | 73 | func resourceVulnDelete(d *schema.ResourceData, m interface{}) error { 74 | apiClient := m.(*client.Client) 75 | 76 | body := models.SystemBody{} 77 | body.Schedule.Cron = "" 78 | body.Schedule.Type = "None" 79 | 80 | _, _, _, err := apiClient.SendRequest("PUT", models.PathVuln, body, 200) 81 | if err != nil { 82 | return err 83 | } 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /provider/resource_labels.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/goharbor/terraform-provider-harbor/client" 8 | "github.com/goharbor/terraform-provider-harbor/models" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | ) 11 | 12 | func resourceLabel() *schema.Resource { 13 | return &schema.Resource{ 14 | Schema: map[string]*schema.Schema{ 15 | "name": { 16 | Type: schema.TypeString, 17 | Required: true, 18 | }, 19 | "description": { 20 | Type: schema.TypeString, 21 | Optional: true, 22 | }, 23 | "color": { 24 | Type: schema.TypeString, 25 | Optional: true, 26 | Default: "false", 27 | }, 28 | "project_id": { 29 | Type: schema.TypeString, 30 | Optional: true, 31 | }, 32 | "scope": { 33 | Type: schema.TypeString, 34 | Computed: true, 35 | }, 36 | }, 37 | Create: resourceLabelCreate, 38 | Read: resourceLabelRead, 39 | Update: resourceLabelUpdate, 40 | Delete: resourceLabelDelete, 41 | Importer: &schema.ResourceImporter{ 42 | State: schema.ImportStatePassthrough, 43 | }, 44 | } 45 | } 46 | 47 | func resourceLabelCreate(d *schema.ResourceData, m interface{}) error { 48 | apiClient := m.(*client.Client) 49 | body := client.LabelsBody(d) 50 | 51 | _, headers, _, err := apiClient.SendRequest("POST", models.PathLabel, body, 201) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | id, err := client.GetID(headers) 57 | d.SetId(id) 58 | return resourceLabelRead(d, m) 59 | } 60 | 61 | func resourceLabelRead(d *schema.ResourceData, m interface{}) error { 62 | apiClient := m.(*client.Client) 63 | 64 | resp, _, respCode, err := apiClient.SendRequest("GET", d.Id(), nil, 200) 65 | if respCode == 404 && err != nil { 66 | d.SetId("") 67 | return nil 68 | } else if err != nil { 69 | return fmt.Errorf("resource not found %s", d.Id()) 70 | } 71 | 72 | var jsonData models.Labels 73 | err = json.Unmarshal([]byte(resp), &jsonData) 74 | if err != nil { 75 | return fmt.Errorf("Resource not found %s", d.Id()) 76 | } 77 | 78 | d.Set("name", jsonData.Name) 79 | d.Set("description", jsonData.Description) 80 | d.Set("color", jsonData.Color) 81 | d.Set("scope", jsonData.Scope) 82 | 83 | return nil 84 | } 85 | 86 | func resourceLabelUpdate(d *schema.ResourceData, m interface{}) error { 87 | apiClient := m.(*client.Client) 88 | body := client.LabelsBody(d) 89 | 90 | _, _, _, err := apiClient.SendRequest("PUT", d.Id(), body, 200) 91 | if err != nil { 92 | return err 93 | } 94 | 95 | return resourceLabelRead(d, m) 96 | } 97 | 98 | func resourceLabelDelete(d *schema.ResourceData, m interface{}) error { 99 | apiClient := m.(*client.Client) 100 | 101 | _, _, respCode, err := apiClient.SendRequest("DELETE", d.Id(), nil, 200) 102 | if respCode != 404 && err != nil { // We can't delete something that doesn't exist. Hence the 404-check 103 | return err 104 | } 105 | return nil 106 | } 107 | -------------------------------------------------------------------------------- /provider/resource_log_rotation_test.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 8 | ) 9 | 10 | const resourcePurgeAuditMain = "harbor_purge_audit_log.main" 11 | 12 | func TestAccPurgeAuditUpdate(t *testing.T) { 13 | resource.Test(t, resource.TestCase{ 14 | PreCheck: func() { testAccPreCheck(t) }, 15 | Providers: testAccProviders, 16 | // CheckDestroy: testAccCheckLabelDestroy, 17 | Steps: []resource.TestStep{ 18 | { 19 | Config: testAccCheckPurgeAuditBasic(), 20 | Check: resource.ComposeTestCheckFunc( 21 | testAccCheckResourceExists(resourcePurgeAuditMain), 22 | resource.TestCheckResourceAttr( 23 | resourcePurgeAuditMain, "schedule", "Daily"), 24 | resource.TestCheckResourceAttr( 25 | resourcePurgeAuditMain, "audit_retention_hour", "24"), 26 | resource.TestCheckResourceAttr( 27 | resourcePurgeAuditMain, "include_operations", "create,pull"), 28 | ), 29 | }, 30 | }, 31 | }) 32 | } 33 | 34 | func testAccCheckPurgeAuditBasic() string { 35 | return fmt.Sprintf(` 36 | resource "harbor_purge_audit_log" "main" { 37 | schedule = "Daily" 38 | audit_retention_hour = 24 39 | include_operations = "create,pull" 40 | } 41 | `) 42 | } 43 | -------------------------------------------------------------------------------- /provider/resource_registry_test.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/goharbor/terraform-provider-harbor/client" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 10 | ) 11 | 12 | const harborRegistryMain = "harbor_registry.main" 13 | 14 | func TestAccRegistryBasic(t *testing.T) { 15 | resource.Test(t, resource.TestCase{ 16 | PreCheck: func() { testAccPreCheck(t) }, 17 | Providers: testAccProviders, 18 | CheckDestroy: testAccCheckRegistryDestroy, 19 | Steps: []resource.TestStep{ 20 | { 21 | Config: testAccCheckRegistryBasic(), 22 | Check: resource.ComposeTestCheckFunc( 23 | 24 | testAccCheckResourceExists(harborRegistryMain), 25 | resource.TestCheckResourceAttr( 26 | harborRegistryMain, "name", "docker-hub-test-reg"), 27 | ), 28 | }, 29 | { 30 | Config: testAccCheckRegistryUpdate(), 31 | Check: resource.ComposeTestCheckFunc( 32 | testAccCheckResourceExists(harborRegistryMain), 33 | resource.TestCheckResourceAttr( 34 | harborRegistryMain, "name", "docker-hub-test-update"), 35 | ), 36 | }, 37 | }, 38 | }) 39 | } 40 | 41 | func testAccCheckRegistryDestroy(s *terraform.State) error { 42 | apiClient := testAccProvider.Meta().(*client.Client) 43 | 44 | for _, rs := range s.RootModule().Resources { 45 | if rs.Type != "harbor_registry" { 46 | continue 47 | } 48 | 49 | resp, _, _, err := apiClient.SendRequest("GET", rs.Primary.ID, nil, 404) 50 | if err != nil { 51 | return fmt.Errorf("Resouse was not delete \n %s", resp) 52 | } 53 | 54 | } 55 | 56 | return nil 57 | } 58 | 59 | func testAccCheckRegistryBasic() string { 60 | // endpoint := os.Getenv("HARBOR_REPLICATION_ENDPOINT") 61 | endpoint := "https://hub.docker.com" 62 | config := fmt.Sprintf(` 63 | 64 | resource "harbor_registry" "main" { 65 | provider_name = "docker-hub" 66 | name = "docker-hub-test-reg" 67 | endpoint_url = "%s" 68 | } 69 | 70 | `, endpoint) 71 | return config 72 | } 73 | 74 | func testAccCheckRegistryUpdate() string { 75 | // endpoint := os.Getenv("HARBOR_REPLICATION_ENDPOINT") 76 | endpoint := "https://hub.docker.com" 77 | config := fmt.Sprintf(` 78 | 79 | resource "harbor_registry" "main" { 80 | provider_name = "docker-hub" 81 | name = "docker-hub-test-update" 82 | endpoint_url = "%s" 83 | } 84 | 85 | `, endpoint) 86 | return config 87 | } 88 | -------------------------------------------------------------------------------- /provider/resource_user.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/goharbor/terraform-provider-harbor/client" 8 | "github.com/goharbor/terraform-provider-harbor/models" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | ) 11 | 12 | func resourceUser() *schema.Resource { 13 | return &schema.Resource{ 14 | Schema: map[string]*schema.Schema{ 15 | "username": { 16 | Type: schema.TypeString, 17 | Required: true, 18 | ForceNew: true, 19 | }, 20 | "password": { 21 | Type: schema.TypeString, 22 | Required: true, 23 | Sensitive: true, 24 | }, 25 | "full_name": { 26 | Type: schema.TypeString, 27 | Required: true, 28 | }, 29 | "email": { 30 | Type: schema.TypeString, 31 | Required: true, 32 | }, 33 | "admin": { 34 | Type: schema.TypeBool, 35 | Optional: true, 36 | Default: false, 37 | }, 38 | "comment": { 39 | Type: schema.TypeString, 40 | Optional: true, 41 | }, 42 | }, 43 | Create: resourceUserCreate, 44 | Read: resourceUserRead, 45 | Update: resourceUserUpdate, 46 | Delete: resourceUserDelete, 47 | Importer: &schema.ResourceImporter{ 48 | State: schema.ImportStatePassthrough, 49 | }, 50 | } 51 | } 52 | 53 | func resourceUserCreate(d *schema.ResourceData, m interface{}) error { 54 | apiClient := m.(*client.Client) 55 | 56 | body := client.UserBody(d) 57 | 58 | _, header, _, err := apiClient.SendRequest("POST", models.PathUsers, &body, 201) 59 | if err != nil { 60 | return err 61 | } 62 | 63 | id, err := client.GetID(header) 64 | if err != nil { 65 | return nil 66 | } 67 | 68 | d.SetId(id) 69 | return resourceUserRead(d, m) 70 | } 71 | 72 | func resourceUserRead(d *schema.ResourceData, m interface{}) error { 73 | apiClient := m.(*client.Client) 74 | resp, _, _, err := apiClient.SendRequest("GET", d.Id(), nil, 200) 75 | if err != nil { 76 | return err 77 | } 78 | var jsonData models.UserBody 79 | err = json.Unmarshal([]byte(resp), &jsonData) 80 | if err != nil { 81 | return fmt.Errorf("Resource not found %s", d.Id()) 82 | } 83 | 84 | d.Set("username", jsonData.Username) 85 | d.Set("full_name", jsonData.Realname) 86 | d.Set("email", jsonData.Email) 87 | d.Set("admin", jsonData.SysadminFlag) 88 | d.Set("comment", jsonData.Comment) 89 | 90 | return nil 91 | } 92 | 93 | func resourceUserUpdate(d *schema.ResourceData, m interface{}) error { 94 | apiClient := m.(*client.Client) 95 | 96 | body := client.UserBody(d) 97 | _, _, _, err := apiClient.SendRequest("PUT", d.Id(), body, 200) 98 | if err != nil { 99 | return err 100 | } 101 | 102 | _, _, _, err = apiClient.SendRequest("PUT", d.Id()+"/sysadmin", body, 200) 103 | if err != nil { 104 | return err 105 | } 106 | 107 | if d.HasChange("password") == true { 108 | _, _, _, err = apiClient.SendRequest("PUT", d.Id()+"/password", body, 200) 109 | if err != nil { 110 | return err 111 | } 112 | } 113 | 114 | return resourceUserRead(d, m) 115 | } 116 | 117 | func resourceUserDelete(d *schema.ResourceData, m interface{}) error { 118 | apiClient := m.(*client.Client) 119 | 120 | _, _, _, err := apiClient.SendRequest("DELETE", d.Id(), nil, 200) 121 | if err != nil { 122 | return err 123 | } 124 | return nil 125 | } 126 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=terraform-provider-harbor 2 | sonar.organization=goharbor 3 | 4 | # This is the name and version displayed in the SonarCloud UI. 5 | #sonar.projectName=terraform-provider-harbor 6 | #sonar.projectVersion=1.0 7 | 8 | # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. 9 | #sonar.sources=. 10 | 11 | # Encoding of the source code. Default is default system encoding 12 | #sonar.sourceEncoding=UTF-8 13 | -------------------------------------------------------------------------------- /templates/data-sources/groups.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_groups Data Source - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_groups (Data Source) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/data-sources/harbor_groups/data-source.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Optional 24 | 25 | - `group_name` (String) The name of the group to filter by. 26 | - `ldap_group_dn` (String) The LDAP group DN to filter by. 27 | 28 | ### Read-Only 29 | 30 | - `groups` (List of Object) (see [below for nested schema](#nestedatt--groups)) 31 | - `id` (String) The ID of this resource. 32 | 33 | 34 | 35 | ### Nested Schema for `groups` 36 | 37 | A list of groups matching the previous arguments. Each `group` object provides the attributes documented below. 38 | 39 | Read-Only: 40 | 41 | - `group_name` (String) The name of the group. 42 | - `group_type` (Number) The type of the group. 43 | - `id` (Number) The ID of the group. 44 | - `ldap_group_dn` (String) The LDAP group DN of the group. 45 | 46 | This data source retrieves a list of Harbor groups and filters them based on the `group_name` and `ldap_group_dn` arguments. It returns a list of `group` objects, each containing the `id`, `group_name`, `group_type`, and `ldap_group_dn` attributes of a group that matches the filter criteria. 47 | -------------------------------------------------------------------------------- /templates/data-sources/project.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_project Data Source - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_project (Data Source) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/data-sources/harbor_project/data-source.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `name` (String) The name of the project. 26 | 27 | ### Read-Only 28 | 29 | - `id` (String) The ID of this resource. 30 | - `project_id` (Number) The id of the project within harbor. 31 | - `public` (Boolean) If the project has public accessibility. 32 | - `type` (String) The type of the project : Project or ProxyCache. 33 | - `vulnerability_scanning` (Boolean) If the images is scanned for vulnerabilities when push to harbor. 34 | -------------------------------------------------------------------------------- /templates/data-sources/project_member_groups.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_project_member_groups Data Source - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_project_member_groups (Data Source) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/data-sources/harbor_project_member_groups/data-source.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `project_id` (String) The id of the project within harbor. 26 | 27 | ### Read-Only 28 | 29 | - `id` (String) The ID of this resource. 30 | - `project_member_groups` (List of Object) (see [below for nested schema](#nestedatt--project_member_groups)) 31 | 32 | 33 | 34 | ### Nested Schema for `project_member_groups` 35 | 36 | A list of project member group matching the previous arguments. Each `project_member_group` object provides the attributes documented below. 37 | 38 | Read-Only: 39 | 40 | - `id` (Number) The ID of this resource. 41 | - `project_id` (String) The id of the project that the group has access to. 42 | - `group_name` (String) The name of the group. 43 | - `role` (String) The permissions that the group is granted. 44 | 45 | This data source retrieves a list of Harbor project member groups and filters them based on the `project_id` argument. It returns a list of `project_member_group` objects, each containing the `id`, `project_id`, `group_name` and `role` attributes of a project member group that matches the filter criteria. 46 | -------------------------------------------------------------------------------- /templates/data-sources/project_member_users.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_project_member_users Data Source - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_project_member_users (Data Source) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/data-sources/harbor_project_member_users/data-source.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `project_id` (String) The id of the project within harbor. 26 | 27 | ### Read-Only 28 | 29 | - `id` (String) The ID of this resource. 30 | - `project_member_users` (List of Object) (see [below for nested schema](#nestedatt--project_member_users)) 31 | 32 | 33 | 34 | ### Nested Schema for `project_member_users` 35 | 36 | A list of project member user matching the previous arguments. Each `project_member_user` object provides the attributes documented below. 37 | 38 | Read-Only: 39 | 40 | - `id` (Number) The ID of this resource. 41 | - `project_id` (String) The id of the project that the user has access to. 42 | - `user_name` (String) The name of the user. 43 | - `role` (String) The permissions that the user is granted. 44 | 45 | This data source retrieves a list of Harbor project member users and filters them based on the `project_id` argument. It returns a list of `project_member_user` objects, each containing the `id`, `project_id`, `user_name` and `role` attributes of a project member user that matches the filter criteria. 46 | -------------------------------------------------------------------------------- /templates/data-sources/projects.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_projects Data Source - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_projects (Data Source) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/data-sources/harbor_projects/data-source.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Optional 24 | 25 | - `name` (String) The name of the project. 26 | - `public` (Boolean) If the project has public accessibility. 27 | - `type` (String) The type of the project : Project or ProxyCache. 28 | - `vulnerability_scanning` (Boolean) If the images will be scanned for vulnerabilities when push to harbor. 29 | 30 | ### Read-Only 31 | 32 | - `id` (String) The ID of this resource. 33 | - `projects` (List of Object) (see [below for nested schema](#nestedatt--projects)) 34 | 35 | 36 | 37 | ### Nested Schema for `projects` 38 | 39 | Read-Only: 40 | 41 | - `name` (String) 42 | - `project_id` (Number) 43 | - `public` (Boolean) 44 | - `type` (String) 45 | - `vulnerability_scanning` (Boolean) 46 | -------------------------------------------------------------------------------- /templates/data-sources/registry.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_registry Data Source - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_registry (Data Source) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/data-sources/harbor_registry/data-source.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `name` (String) The name of the register. 26 | 27 | ### Read-Only 28 | 29 | - `description` (String) The description of the external container register. 30 | - `id` (String) The ID of this resource. 31 | - `insecure` (Boolean) If the certificate of the external container register can be verified. 32 | - `registry_id` (Number) The id of the register within harbor. 33 | - `status` (String) The health status of the external container register 34 | - `type` (String) The type of the provider type. 35 | - `url` (String) The url endpoint for the external container register 36 | -------------------------------------------------------------------------------- /templates/data-sources/robot_accounts.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_robot_accounts Data Source - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_robot_accounts (Data Source) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/data-sources/harbor_robot_accounts/data-source.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Optional 24 | 25 | - `name` (String) The name of the robot account to filter by. 26 | - `level` (String) Level of the robot account, currently either `system` or `project`. Default is `system`. 27 | - `project_id` (Number) The id of the project within harbor. 28 | 29 | ### Read-Only 30 | 31 | - `id` (String) The ID of this resource. 32 | - `robot_accounts` (List of Object) (see [below for nested schema](#nestedatt--robot_accounts)) 33 | 34 | 35 | 36 | ### Nested Schema for `robot_accounts` 37 | 38 | A list of robot accounts matching the previous arguments. Each `robot_account` object provides the attributes documented below. 39 | 40 | Read-Only: 41 | 42 | - `id` (String) The ID of the robot account. 43 | - `name` (String) The name of the robot account. 44 | - `description` (String) The description of the robot account. 45 | - `level` (String) The level of the robot account, currently either `system` or `project`. 46 | - `duration` (Number) The number of days before the robot account expires. A value of `-1` means the robot account will not expire. 47 | - `disable` (Boolean) Indicates whether the robot account is disabled. 48 | 49 | This data source retrieves a list of Harbor robot accounts and filters them based on the `name`, `level` and `project_id` arguments. It returns a list of `robot_account` objects, each containing the `id`, `name`, `description`, `level`, `duration` and `disable` attributes of a robot account that matches the filter criteria. 50 | -------------------------------------------------------------------------------- /templates/data-sources/users.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_users Data Source - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_users (Data Source) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/data-sources/harbor_users/data-source.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Optional 24 | 25 | - `username` (String) The name of the user to filter by. 26 | - `email` (String) The email of the user to filter by. 27 | 28 | ### Read-Only 29 | 30 | - `id` (String) The ID of this resource. 31 | - `users` (List of Object) (see [below for nested schema](#nestedatt--users)) 32 | 33 | 34 | 35 | ### Nested Schema for `users` 36 | 37 | A list of users matching the previous arguments. Each `user` object provides the attributes documented below. 38 | 39 | Read-Only: 40 | 41 | - `id` (String) The ID of the user. 42 | - `username` (String) The name of the user. 43 | - `full_name` (String) The full name of the user. 44 | - `email` (String) The email of the user. 45 | - `admin` (Boolean) Indicates whether the user is an admin. 46 | - `comment` (String) A comment about the user. 47 | 48 | This data source retrieves a list of Harbor users and filters them based on the `username` and `email` arguments. It returns a list of `user` objects, each containing the `id`, `username`, `full_name`, `email`, `admin` and `comment` attributes of a user that matches the filter criteria. 49 | -------------------------------------------------------------------------------- /templates/index.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor Provider" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor Provider 14 | 15 | 16 | 17 | ## Schema 18 | 19 | ### Required 20 | 21 | - `url` (String) The url of harbor 22 | - `password` (String) The password to be used to access harbor 23 | - `username` (String) The username to be used to access harbor 24 | 25 | ### Optional 26 | 27 | - `api_version` (Number) Choose which version of the api you would like to use 1 or 2 (default is 2) 28 | - `bearer_token` (String) The bearer token to be used to access harbor. Will take precedence over username and password if set 29 | - `session_id` (String) The session ID cookie (sid) when using OAuth/OIDC. Can be provided via `HARBOR_SESSION_ID`. Will take precedence over `bearer_token` if set. Note that OAuth/OIDC support is experimental and will be deprecated and removed if harbor provides a better way to authenticate with its API 30 | - `insecure` (Boolean) Choose to ignore certificate errors 31 | - `robot_prefix` (String) Without this option, the provider will try to automatically determine the robot prefix with a call to the admin api. If you don't have admin access and want to create system robot account, you'll have to set this value. 32 | 33 | ### Environment variables 34 | 35 | The provider can also be configured via environment variables: `HARBOR_URL`, `HARBOR_USERNAME`, `HARBOR_PASSWORD`, `HARBOR_SESSION_ID`. 36 | -------------------------------------------------------------------------------- /templates/resources/config_security.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_config_security Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_config_security (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/resources/harbor_config_security/resource.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `cve_allowlist` (List of String) System allowlist. Vulnerabilities in this list will be ignored when pushing and pulling images. Should be in the format or `["CVE-123", "CVE-145"]` or `["CVE-123"]` 26 | 27 | ### Optional 28 | 29 | - `expires_at` (Number) The time for expiration of the allowlist, in the form of seconds since epoch. This is an optional attribute, if it's not set the CVE allowlist does not expire. 30 | 31 | ### Read-Only 32 | 33 | - `creation_time` (String) Time of creation of the list. 34 | - `id` (String) The ID of this resource. 35 | - `update_time` (String) Time of update of the list. 36 | 37 | ## Import 38 | Import is supported using the following syntax with the `registry` `id`: 39 | 40 | ```shell 41 | # import using the id of the repo 42 | terraform import harbor_config_security.main "7" 43 | ``` 44 | Note that at this point of time Harbor doesn't has any api endpoint for deleting this list. Only updating the records. 45 | -------------------------------------------------------------------------------- /templates/resources/config_system.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_config_system Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_config_system (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/resources/harbor_config_system/resource.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Optional 24 | 25 | - `project_creation_restriction` (String) Who can create projects within Harbor. Can be `"adminonly"` or `"everyone"` 26 | - `read_only` (Boolean) Whether or not the system is in read only mode. 27 | - `robot_name_prefix` (String) Robot account prefix. 28 | - `robot_token_expiration` (Number) The amount of time in days a robot account will expire. 29 | - `scanner_skip_update_pulltime` (Boolean) Whether or not to skip update pull time for scanner. 30 | - `storage_per_project` (Number) Default quota space per project in GIB. Default is -1 (unlimited). 31 | - `audit_log_forward_endpoint` (String) The endpoint to forward audit logs to. 32 | - `skip_audit_log_database` (Boolean) Whether or not to skip audit log database. 33 | - `banner_message` (Block Set) (see [below for nested schema](#nestedblock--banner_message)) 34 | 35 | 36 | 37 | ### Nested Schema for `banner_message` 38 | 39 | ### Required 40 | - `message` (String) The message to display in the banner. 41 | 42 | ### Optional 43 | - `closable` (Boolean) Whether or not the banner message is closable. 44 | - `type` (String) The type of banner message. Can be `"info"`, `"warning"`, `"success"` or `"danger"`. 45 | - `from_date` (String) The date the banner message will start displaying. (Format: `MM/DD/YYYY`) 46 | - `to_date` (String) The date the banner message will stop displaying. (Format: `MM/DD/YYYY`) 47 | 48 | #### Notes 49 | `scanner_skip_update_pulltime` can only be used with harbor version v2.8.0 and above 50 | 51 | `robot_token_expiration` if the time is set to high you will get a 500 internal server error message when creating robot accounts 52 | 53 | ### Read-Only 54 | 55 | - `id` (String) The ID of this resource. 56 | -------------------------------------------------------------------------------- /templates/resources/garbage_collection.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_garbage_collection Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_garbage_collection (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/resources/harbor_garbage_collection/resource.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `schedule` (String) Sets the schedule how often the Garbage Collection will run. Can be to `"hourly"`, `"daily"`, `"weekly"` or can be a custom cron string ie, `"0 5 4 * * *"` 26 | 27 | ### Optional 28 | 29 | - `delete_untagged` (Boolean) Allow garbage collection on untagged artifacts. 30 | - `workers` (Number) Number of workers to run the garbage collection, value must be between 1 and 5. 31 | 32 | ### Read-Only 33 | 34 | - `id` (String) The ID of this resource. 35 | -------------------------------------------------------------------------------- /templates/resources/group.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_group Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_group (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/resources/harbor_group/resource.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `group_name` (String) The name of the group. 26 | - `group_type` (Number) 3. Note: group type 3 is OIDC group. 27 | 28 | ### Optional 29 | 30 | - `ldap_group_dn` (String) The distinguished name of the group within AD/LDAP. 31 | 32 | ### Read-Only 33 | 34 | - `id` (String) The ID of this resource. 35 | 36 | ## Import 37 | Import is supported using the following syntax with the `group` `id`: 38 | 39 | ```shell 40 | terraform import harbor_group.storage-group /usergroups/19 41 | ``` 42 | -------------------------------------------------------------------------------- /templates/resources/immutable_tag_rule.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_immutable_tag_rule Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_immutable_tag_rule (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/resources/harbor_immutable_tag_rule/resource.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `project_id` (String) The project id of which you would like to apply this policy. 26 | 27 | ### Optional 28 | 29 | - `disabled` (Boolean) Specify if the rule is disable or not. Defaults to `false` 30 | - `repo_excluding` (String) For the repositories excluding. 31 | - `repo_matching` (String) For the repositories matching. 32 | - `tag_excluding` (String) For the tag excluding. 33 | - `tag_matching` (String) For the tag matching. 34 | 35 | ### Read-Only 36 | 37 | - `id` (String) The ID of this resource. 38 | 39 | ## Import 40 | Import is supported using the following syntax with the `project` and `immutabletagrules` `id`'s: 41 | 42 | ```shell 43 | terraform import harbor_immutable_tag_rule.main /projects/4/immutabletagrules/25 44 | ``` 45 | -------------------------------------------------------------------------------- /templates/resources/interrogation_services.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_interrogation_services Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_interrogation_services (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/resources/harbor_interrogation_services/resource.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `vulnerability_scan_policy` (String) The frequency of the vulnerability scanning is done. This can be `Daily`, `Weekly`, `Monthly` or can be a custom cron string. 26 | 27 | ### Optional 28 | 29 | - `default_scanner` (String) Sets the default interrogation service `"Clair"` 30 | 31 | ### Read-Only 32 | 33 | - `id` (String) The ID of this resource. 34 | -------------------------------------------------------------------------------- /templates/resources/label.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_label Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_label (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | ### Global 20 | 21 | {{tffile "examples/resources/harbor_label/global.tf"}} 22 | 23 | ### Project 24 | 25 | {{tffile "examples/resources/harbor_label/project.tf"}} 26 | 27 | ## Schema 28 | 29 | ### Required 30 | 31 | - `name` (String) The of name of the label within harbor. 32 | 33 | ### Optional 34 | 35 | - `color` (String) The color of the label within harbor (Default: #FFFFF) 36 | - `description` (String) The Description of the label within harbor 37 | - `project_id` (String) The id of the project with harbor. 38 | 39 | ### Read-Only 40 | 41 | - `id` (String) The ID of this resource. 42 | - `scope` (String) 43 | 44 | ## Import 45 | Import is supported using the following syntax with the `label` `id`: 46 | 47 | ```shell 48 | terraform import harbor_label.main /labels/1 49 | ``` 50 | -------------------------------------------------------------------------------- /templates/resources/preheat_instance.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_preheat_instance Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_preheat_instance (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | ### Basic Usage 20 | 21 | {{tffile "examples/resources/harbor_preheat_instance/basic.tf"}} 22 | 23 | ### Usage with Authentication 24 | 25 | {{tffile "examples/resources/harbor_preheat_instance/authentification.tf"}} 26 | 27 | ## Schema 28 | 29 | ### Required 30 | 31 | - `name` (String) The name of the preheat instance. 32 | - `vendor` (String) The vendor of the preheat instance. Must be either "dragonfly" or "kraken". 33 | - `endpoint` (String) The endpoint of the preheat instance. 34 | 35 | ### Optional 36 | 37 | - `description` (String) The description of the preheat instance. Defaults to an empty string. 38 | - `auth_mode` (String) The authentication mode for the preheat instance. Must be either "NONE", "BASIC", or "OAUTH". Defaults to "NONE". 39 | - `username` (String) The username for the preheat instance. Required if `auth_mode` is "BASIC". Defaults to an empty string. 40 | - `password` (String, Sensitive) The password for the preheat instance. Required if `auth_mode` is "BASIC". Defaults to an empty string. 41 | - `token` (String, Sensitive) The token for the preheat instance. Required if `auth_mode` is "OAUTH". Defaults to an empty string. 42 | - `default` (Boolean) Whether the preheat instance is the default instance. Defaults to false. 43 | - `enabled` (Boolean) Whether the preheat instance is enabled. Defaults to true. 44 | - `insecure` (Boolean) Whether to allow insecure connections to the preheat instance. Defaults to false. 45 | 46 | ### Read-Only 47 | 48 | - `id` (String) The ID of the preheat instance. 49 | 50 | ## Import 51 | 52 | The `harbor_preheat_instance` resource can be imported using the preheat instance ID. 53 | 54 | ```sh 55 | terraform import harbor_preheat_instance.example /p2p/preheat/instances/example-preheat-instance 56 | ``` -------------------------------------------------------------------------------- /templates/resources/project.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_project Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_project (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | ### Hosted 20 | 21 | {{tffile "examples/resources/harbor_project/resource.tf"}} 22 | 23 | ### Proxy 24 | 25 | {{tffile "examples/resources/harbor_project/proxy.tf"}} 26 | 27 | ## Schema 28 | 29 | ### Required 30 | 31 | - `name` (String) The name of the project that will be created in harbor. 32 | 33 | ### Optional 34 | 35 | - `cve_allowlist` (List of String) Project allowlist allows vulnerabilities in this list to be ignored in this project when pushing and pulling images. Should be in the format or `["CVE-123", "CVE-145"]` or `["CVE-123"]` 36 | - `deployment_security` (String) Prevent deployment of images with vulnerability severity equal or higher than the specified value. Images must be scanned before this takes effect. Possible values: `"critical"`, `"high"`, `"medium"`, `"low"`, `"none"`. (Default: `""` - empty) 37 | - `enable_content_trust` (Boolean) Enables Content Trust for project. When enabled it queries the embedded docker notary server. (Default: `false`). 38 | - `enable_content_trust_cosign` (Boolean) Enables Content Trust Cosign for project. When enabled it queries Cosign. (Default: `false`) 39 | - `force_destroy` (Boolean) A boolean that indicates all repositories should be deleted from the project so that the project can be destroyed without error. These repositories are *not* recoverable. 40 | - `public` (Boolean) The project will be public accessibility.(Default: `false`) 41 | - `registry_id` (Number) To enable project as Proxy Cache. 42 | - `storage_quota` (Number) The storage quota of the project in GB's. 43 | - `vulnerability_scanning` (Boolean) Images will be scanned for vulnerabilities when push to harbor. (Default: `true`) 44 | - `auto_sbom_generation` (Boolean) Automatically generate SBOM for images pushed to this project. (Default: `false`) can only be used with Harbor version v2.11.0 and above 45 | 46 | ### Read-Only 47 | 48 | - `id` (String) The ID of this resource. 49 | - `project_id` (Number) The project id of this resource. 50 | 51 | ## Import 52 | Import is supported using the following syntax with the `project` `id`: 53 | 54 | ```shell 55 | terraform import harbor_project.main /projects/1 56 | ``` 57 | -------------------------------------------------------------------------------- /templates/resources/project_member_group.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_project_member_group Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_project_member_group (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/resources/harbor_project_member_group/resource.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `project_id` (String) The project id of the project that the entity will have access to. 26 | - `role` (String) The permissions that the entity will be granted. 27 | - `type` (String) The group type. Can be set to `"ldap"`, `"internal"` or `"oidc"`. 28 | 29 | #### Notes 30 | `type` can only be `oidc` when used with harbor version v1.10.1 and above. 31 | 32 | ### Optional 33 | 34 | - `group_id` (Number) 3. Note: group type 3 is OIDC group. 35 | - `group_name` (String) The name of the group member entity. 36 | - `ldap_group_dn` (String) The distinguished name of the group within AD/LDAP. 37 | 38 | ### Read-Only 39 | 40 | - `id` (String) The ID of this resource. 41 | - `member_id` (Number) 42 | 43 | ## Import 44 | Import is supported using the following syntax with the `project` and `member` `id`'s: 45 | 46 | ```shell 47 | terraform import harbor_project_member_group.main /projects/10/members/200 48 | ``` 49 | -------------------------------------------------------------------------------- /templates/resources/project_member_user.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_project_member_user Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_project_member_user (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/resources/harbor_project_member_user/resource.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `project_id` (String) The project id of the project that the entity will have access to. 26 | - `role` (String) The permissions that the entity will be granted. 27 | - `user_name` (String) The name of the member entity. 28 | 29 | ### Read-Only 30 | 31 | - `id` (String) The ID of this resource. 32 | - `member_id` (Number) The member id of the member. 33 | 34 | ## Import 35 | Import is supported using the following syntax with the `project` and `member` `id`'s: 36 | 37 | ```shell 38 | terraform import harbor_project_member_user.main /projects/10/members/200 39 | ``` 40 | -------------------------------------------------------------------------------- /templates/resources/project_webhook.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_project_webhook Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_project_webhook (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/resources/harbor_project_webhook/resource.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `address` (String) The address of the webhook. 26 | - `events_types` (List of String) The type events you want to subscript to can be 27 | - `name` (String) The name of the webhook that will be created in harbor. 28 | - `notify_type` (String) The notification type either `http` or `slack`. 29 | - `project_id` (String) The project id of the harbor that webhook related to. 30 | 31 | ##### `events_types` Options 32 | 33 | - `DELETE_ARTIFACT` 34 | - `PULL_ARTIFACT` 35 | - `PUSH_ARTIFACT` 36 | - `QUOTA_EXCEED` 37 | - `QUOTA_WARNING` 38 | - `REPLICATION` 39 | - `SCANNING_FAILED` 40 | - `SCANNING_COMPLETED` 41 | - `TAG_RETENTION` 42 | 43 | ### Optional 44 | 45 | - `auth_header` (String) authentication header for you the webhook. 46 | - `description` (String) A description of the webhook. 47 | - `enabled` (Boolean) To enable / disable the webhook. Default `true`. 48 | - `skip_cert_verify` (Boolean) checks the for validate SSL certificate. 49 | 50 | ### Read-Only 51 | 52 | - `id` (String) The ID of this resource. 53 | -------------------------------------------------------------------------------- /templates/resources/purge_audit_log.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_purge_audit_log Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_purge_audit_log (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/resources/harbor_purge_audit_log/resource.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `audit_retention_hour` (Number) To configure how long audit logs should be kept. For example, if you set this to 24 Harbor will only purge audit logs that are 24 or more hours old. 26 | - `include_operations` (String) Valid values are `create` `delete` `pull`, thoses values can be comma separated. When Create, Delete, or Pull is set, Harbor will include audit logs for those operations in the purge. 27 | - `schedule` (String) Sets the schedule how often the Garbage Collection will run. Can be to `"Hourly"`, `"Daily"`, `"Weekly"` or can be a custom cron string ie, `"5 4 * * *"` 28 | 29 | ### Read-Only 30 | 31 | - `id` (String) The ID of this resource. 32 | -------------------------------------------------------------------------------- /templates/resources/registry.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_registry Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_registry (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/resources/harbor_registry/resource.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `endpoint_url` (String) The url endpoint for the external container register ie `"https://hub.docker.com"` 26 | - `name` (String) The name of the register. 27 | - `provider_name` (String) The name of the provider. 28 | 29 | ##### `provider_name` Options 30 | 31 | - `alibaba` 32 | - `artifact-hub` 33 | - `aws` 34 | - `azure` 35 | - `docker-hub` 36 | - `docker-registry` 37 | - `gitlab` 38 | - `github` 39 | - `google` 40 | - `harbor` 41 | - `huawei` 42 | - `jfrog` 43 | - `quay` 44 | 45 | ### Optional 46 | 47 | - `access_id` (String) The username / access id for the external container register. 48 | - `access_secret` (String, Sensitive) The password / access keys / token for the external container register. 49 | - `description` (String) The description of the external container register. 50 | - `insecure` (Boolean) Verifies the certificate of the external container register. (Default: `false`) 51 | 52 | ### Read-Only 53 | 54 | - `id` (String) The ID of this resource. 55 | - `registry_id` (Number) 56 | - `status` (String) 57 | 58 | ## Import 59 | Import is supported using the following syntax with the `registry` `id`: 60 | 61 | ```shell 62 | terraform import harbor_registry.main /registries/7 63 | ``` 64 | -------------------------------------------------------------------------------- /templates/resources/retention_policy.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_retention_policy Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_retention_policy (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/resources/harbor_retention_policy/resource.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `rule` (Block List, Min: 1, Max: 15) (see [below for nested schema](#nestedblock--rule)) 26 | - `scope` (String) The project id of which you would like to apply this policy. 27 | 28 | ### Optional 29 | 30 | - `schedule` (String) The schedule of when you would like the policy to run. This can be `Hourly`, `Daily`, `Weekly` or can be a custom cron string. 31 | 32 | ### Read-Only 33 | 34 | - `id` (String) The ID of this resource. 35 | 36 | 37 | 38 | ### Nested Schema for `rule` 39 | 40 | ~> Multiple tags or repositories must be provided as a comma-separated list wrapped into curly brackets `{ }`. Otherwise, the value is interpreted as a single value. 41 | 42 | Optional: 43 | 44 | - `always_retain` (Boolean) retain always. 45 | - `disabled` (Boolean) Specify if the rule is disable or not. Defaults to `false` 46 | - `most_recently_pulled` (Number) retain the most recently pulled n artifacts. 47 | - `most_recently_pushed` (Number) retain the most recently pushed n artifacts. 48 | - `n_days_since_last_pull` (Number) retains the artifacts pulled within the lasts n days. 49 | - `n_days_since_last_push` (Number) retains the artifacts pushed within the lasts n days. 50 | - `repo_excluding` (String) For the repositories excluding. 51 | - `repo_matching` (String) For the repositories matching. 52 | - `tag_excluding` (String) For the tag excluding. 53 | - `tag_matching` (String) For the tag matching. 54 | - `untagged_artifacts` (Boolean) with untagged artifacts. Defaults to `true` 55 | 56 | ## Import 57 | Import is supported using the following syntax with the `retention_policy` `id`: 58 | 59 | ```shell 60 | terraform import harbor_retention_policy.main /retentions/10 61 | ``` 62 | -------------------------------------------------------------------------------- /templates/resources/tasks.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_tasks Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_tasks (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/resources/harbor_tasks/resource.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `vulnerability_scan_policy` (String) The frequency of the vulnerability scanning is done. Can be to **"hourly"**, **"daily"** or **"weekly"** 26 | 27 | ### Read-Only 28 | 29 | - `id` (String) The ID of this resource. 30 | -------------------------------------------------------------------------------- /templates/resources/user.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "harbor_user Resource - terraform-provider-harbor" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | {{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. 10 | 11 | For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} 12 | 13 | # harbor_user (Resource) 14 | 15 | 16 | 17 | ## Example Usage 18 | 19 | {{tffile "examples/resources/harbor_user/resource.tf"}} 20 | 21 | ## Schema 22 | 23 | ### Required 24 | 25 | - `email` (String) The email address of the internal user. 26 | - `full_name` (String) The Full Name of the internal user. 27 | - `password` (String, Sensitive) The password for the internal user. 28 | - `username` (String) The username of the internal user. 29 | 30 | ### Optional 31 | 32 | - `admin` (Boolean) If the user will have admin rights within Harbor (Default: `false`) 33 | - `comment` (String) Any comments for that are need for the internal user. 34 | 35 | ### Read-Only 36 | 37 | - `id` (String) The ID of this resource. 38 | 39 | ## Import 40 | Import is supported using the following syntax with the `user` `id`: 41 | 42 | ```shell 43 | terraform import harbor_user.main /users/19 44 | ``` 45 | -------------------------------------------------------------------------------- /terraform-registry-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "metadata": { 4 | "protocol_versions": ["5.0"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tools/main.go: -------------------------------------------------------------------------------- 1 | // source: https://github.com/hashicorp/terraform-provider-awscc/blob/main/tools/main.go 2 | 3 | package main 4 | 5 | import ( 6 | _ "github.com/client9/misspell/cmd/misspell" 7 | _ "github.com/golangci/golangci-lint/cmd/golangci-lint" 8 | _ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs" 9 | _ "github.com/pavius/impi/cmd/impi" 10 | ) 11 | --------------------------------------------------------------------------------