From 460fb08afbe2ddcfcc8de54560d677c8d5083cdc Mon Sep 17 00:00:00 2001 From: Travis Leeden Date: Wed, 27 Mar 2024 11:33:04 +1000 Subject: [PATCH] feat: Add scheduled project trigger (#620) Added new resource `project_scheduled_trigger` to managing scheduled project/runbook triggers --- .../azure_cloud_service_deployment_targets.md | 2 + ...rvice_fabric_cluster_deployment_targets.md | 2 + .../azure_web_app_deployment_targets.md | 2 + docs/data-sources/certificates.md | 2 + docs/data-sources/channels.md | 2 + .../cloud_region_deployment_targets.md | 2 + docs/data-sources/deployment_targets.md | 2 + docs/data-sources/environments.md | 2 + docs/data-sources/git_credentials.md | 2 + .../kubernetes_cluster_deployment_targets.md | 2 + docs/data-sources/library_variable_sets.md | 2 + docs/data-sources/lifecycles.md | 2 + .../listening_tentacle_deployment_targets.md | 2 + docs/data-sources/machine.md | 2 + docs/data-sources/machine_policies.md | 2 + ...offline_package_drop_deployment_targets.md | 2 + .../polling_tentacle_deployment_targets.md | 2 + docs/data-sources/project_groups.md | 2 + docs/data-sources/projects.md | 2 + docs/data-sources/script_modules.md | 2 + docs/data-sources/space.md | 2 + docs/data-sources/spaces.md | 2 + .../ssh_connection_deployment_targets.md | 2 + docs/data-sources/tag_sets.md | 2 + docs/data-sources/teams.md | 2 + docs/data-sources/tenants.md | 2 + docs/data-sources/user_roles.md | 2 + docs/data-sources/users.md | 2 + docs/data-sources/variables.md | 2 + docs/data-sources/worker_pools.md | 2 + docs/resources/aws_openid_connect_account.md | 40 ++ .../external_feed_create_release_trigger.md | 14 +- docs/resources/git_credential.md | 2 + docs/resources/library_variable_set.md | 2 + docs/resources/machine_policy.md | 2 + docs/resources/project_scheduled_trigger.md | 201 +++++++ docs/resources/runbook.md | 2 + docs/resources/script_module.md | 35 -- docs/resources/tag.md | 2 + docs/resources/tenant.md | 2 + docs/resources/tenant_common_variable.md | 2 + docs/resources/tenant_project_variable.md | 2 + .../import.sh | 1 + .../resource.tf | 14 + .../import.sh | 1 + .../resource.tf | 70 +++ integration_test.go | 58 ++ octopusdeploy/provider.go | 1 + ...ce_external_feed_create_release_trigger.go | 1 + .../resource_project_scheduled_trigger.go | 119 ++++ .../schema_project_scheduled_trigger.go | 534 ++++++++++++++++++ ...ternal_feed_create_release_trigger.md.tmpl | 23 + .../project_scheduled_trigger.md.tmpl | 23 + .../53-scheduledprojecttrigger/NonTenanted.tf | 219 +++++++ .../53-scheduledprojecttrigger/Tenanted.tf | 169 ++++++ .../53-scheduledprojecttrigger/config.tf | 7 + .../environments.tf | 23 + .../53-scheduledprojecttrigger/provider.tf | 5 + .../provider_vars.tf | 18 + terraform/53-scheduledprojecttrigger/space.tf | 3 + 60 files changed, 1616 insertions(+), 39 deletions(-) create mode 100644 docs/resources/aws_openid_connect_account.md create mode 100644 docs/resources/project_scheduled_trigger.md create mode 100644 examples/resources/octopusdeploy_external_feed_create_release_trigger/import.sh create mode 100644 examples/resources/octopusdeploy_external_feed_create_release_trigger/resource.tf create mode 100644 examples/resources/octopusdeploy_project_scheduled_trigger/import.sh create mode 100644 examples/resources/octopusdeploy_project_scheduled_trigger/resource.tf create mode 100644 octopusdeploy/resource_project_scheduled_trigger.go create mode 100644 octopusdeploy/schema_project_scheduled_trigger.go create mode 100644 templates/resources/external_feed_create_release_trigger.md.tmpl create mode 100644 templates/resources/project_scheduled_trigger.md.tmpl create mode 100644 terraform/53-scheduledprojecttrigger/NonTenanted.tf create mode 100644 terraform/53-scheduledprojecttrigger/Tenanted.tf create mode 100644 terraform/53-scheduledprojecttrigger/config.tf create mode 100644 terraform/53-scheduledprojecttrigger/environments.tf create mode 100644 terraform/53-scheduledprojecttrigger/provider.tf create mode 100644 terraform/53-scheduledprojecttrigger/provider_vars.tf create mode 100644 terraform/53-scheduledprojecttrigger/space.tf diff --git a/docs/data-sources/azure_cloud_service_deployment_targets.md b/docs/data-sources/azure_cloud_service_deployment_targets.md index fc9c0d334..25e99b4fa 100644 --- a/docs/data-sources/azure_cloud_service_deployment_targets.md +++ b/docs/data-sources/azure_cloud_service_deployment_targets.md @@ -182,3 +182,5 @@ Read-Only: - `upgrade_required` (Boolean) - `upgrade_suggested` (Boolean) - `version` (String) + + diff --git a/docs/data-sources/azure_service_fabric_cluster_deployment_targets.md b/docs/data-sources/azure_service_fabric_cluster_deployment_targets.md index d046b6cd1..266adf465 100644 --- a/docs/data-sources/azure_service_fabric_cluster_deployment_targets.md +++ b/docs/data-sources/azure_service_fabric_cluster_deployment_targets.md @@ -185,3 +185,5 @@ Read-Only: - `upgrade_required` (Boolean) - `upgrade_suggested` (Boolean) - `version` (String) + + diff --git a/docs/data-sources/azure_web_app_deployment_targets.md b/docs/data-sources/azure_web_app_deployment_targets.md index 475f4a73a..aecad7291 100644 --- a/docs/data-sources/azure_web_app_deployment_targets.md +++ b/docs/data-sources/azure_web_app_deployment_targets.md @@ -179,3 +179,5 @@ Read-Only: - `upgrade_required` (Boolean) - `upgrade_suggested` (Boolean) - `version` (String) + + diff --git a/docs/data-sources/certificates.md b/docs/data-sources/certificates.md index c0b5270b7..9ba533b85 100644 --- a/docs/data-sources/certificates.md +++ b/docs/data-sources/certificates.md @@ -77,3 +77,5 @@ Read-Only: - `tenants` (List of String) A list of tenant IDs associated with this resource. - `thumbprint` (String) - `version` (Number) + + diff --git a/docs/data-sources/channels.md b/docs/data-sources/channels.md index 804ae11d9..bf1546f4f 100644 --- a/docs/data-sources/channels.md +++ b/docs/data-sources/channels.md @@ -69,3 +69,5 @@ Read-Only: - `deployment_action` (String) - `package_reference` (String) + + diff --git a/docs/data-sources/cloud_region_deployment_targets.md b/docs/data-sources/cloud_region_deployment_targets.md index e2a27a053..dc9610f0b 100644 --- a/docs/data-sources/cloud_region_deployment_targets.md +++ b/docs/data-sources/cloud_region_deployment_targets.md @@ -177,3 +177,5 @@ Read-Only: - `upgrade_required` (Boolean) - `upgrade_suggested` (Boolean) - `version` (String) + + diff --git a/docs/data-sources/deployment_targets.md b/docs/data-sources/deployment_targets.md index c1534eb07..b9cac407b 100644 --- a/docs/data-sources/deployment_targets.md +++ b/docs/data-sources/deployment_targets.md @@ -166,3 +166,5 @@ Read-Only: - `upgrade_required` (Boolean) - `upgrade_suggested` (Boolean) - `version` (String) + + diff --git a/docs/data-sources/environments.md b/docs/data-sources/environments.md index 960c96dd4..69bc1ab23 100644 --- a/docs/data-sources/environments.md +++ b/docs/data-sources/environments.md @@ -78,3 +78,5 @@ Read-Only: Read-Only: - `is_enabled` (Boolean) + + diff --git a/docs/data-sources/git_credentials.md b/docs/data-sources/git_credentials.md index 5645109c5..d5e0820a4 100644 --- a/docs/data-sources/git_credentials.md +++ b/docs/data-sources/git_credentials.md @@ -39,3 +39,5 @@ Read-Only: - `space_id` (String) The space ID associated with this resource. - `type` (String) The Git credential authentication type. - `username` (String) The username for the Git credential. + + diff --git a/docs/data-sources/kubernetes_cluster_deployment_targets.md b/docs/data-sources/kubernetes_cluster_deployment_targets.md index cb7d166e0..e584ccb51 100644 --- a/docs/data-sources/kubernetes_cluster_deployment_targets.md +++ b/docs/data-sources/kubernetes_cluster_deployment_targets.md @@ -254,3 +254,5 @@ Read-Only: Read-Only: - `token_path` (String) + + diff --git a/docs/data-sources/library_variable_sets.md b/docs/data-sources/library_variable_sets.md index a50ca91ef..d2e8f49ff 100644 --- a/docs/data-sources/library_variable_sets.md +++ b/docs/data-sources/library_variable_sets.md @@ -52,3 +52,5 @@ Read-Only: - `id` (String) - `label` (String) - `name` (String) + + diff --git a/docs/data-sources/lifecycles.md b/docs/data-sources/lifecycles.md index 1acbc0b3a..2d6ce478e 100644 --- a/docs/data-sources/lifecycles.md +++ b/docs/data-sources/lifecycles.md @@ -103,3 +103,5 @@ Read-Only: - `quantity_to_keep` (Number) - `should_keep_forever` (Boolean) - `unit` (String) + + diff --git a/docs/data-sources/listening_tentacle_deployment_targets.md b/docs/data-sources/listening_tentacle_deployment_targets.md index ce465ad4e..e8f7ccb8f 100644 --- a/docs/data-sources/listening_tentacle_deployment_targets.md +++ b/docs/data-sources/listening_tentacle_deployment_targets.md @@ -96,3 +96,5 @@ Read-Only: - `upgrade_required` (Boolean) - `upgrade_suggested` (Boolean) - `version` (String) + + diff --git a/docs/data-sources/machine.md b/docs/data-sources/machine.md index 839a607db..b4ce10418 100644 --- a/docs/data-sources/machine.md +++ b/docs/data-sources/machine.md @@ -42,3 +42,5 @@ description: |- - `tenanteddeploymentparticipation` (String) - `tenantids` (List of String) - `tenanttags` (List of String) + + diff --git a/docs/data-sources/machine_policies.md b/docs/data-sources/machine_policies.md index 3d2a3b76b..493450926 100644 --- a/docs/data-sources/machine_policies.md +++ b/docs/data-sources/machine_policies.md @@ -104,3 +104,5 @@ Read-Only: - `calamari_update_behavior` (String) - `tentacle_update_account_id` (String) - `tentacle_update_behavior` (String) + + diff --git a/docs/data-sources/offline_package_drop_deployment_targets.md b/docs/data-sources/offline_package_drop_deployment_targets.md index d55532393..2ddf0e6e1 100644 --- a/docs/data-sources/offline_package_drop_deployment_targets.md +++ b/docs/data-sources/offline_package_drop_deployment_targets.md @@ -177,3 +177,5 @@ Read-Only: - `upgrade_required` (Boolean) - `upgrade_suggested` (Boolean) - `version` (String) + + diff --git a/docs/data-sources/polling_tentacle_deployment_targets.md b/docs/data-sources/polling_tentacle_deployment_targets.md index 1b11bd69f..fad2a39bc 100644 --- a/docs/data-sources/polling_tentacle_deployment_targets.md +++ b/docs/data-sources/polling_tentacle_deployment_targets.md @@ -180,3 +180,5 @@ Read-Only: - `upgrade_required` (Boolean) - `upgrade_suggested` (Boolean) - `version` (String) + + diff --git a/docs/data-sources/project_groups.md b/docs/data-sources/project_groups.md index 3e72b136f..ed88f743a 100644 --- a/docs/data-sources/project_groups.md +++ b/docs/data-sources/project_groups.md @@ -47,3 +47,5 @@ Read-Only: - `name` (String) The name of this resource. - `retention_policy_id` (String) The ID of the retention policy associated with this project group. - `space_id` (String) The space ID associated with this project group. + + diff --git a/docs/data-sources/projects.md b/docs/data-sources/projects.md index 01bd849f6..30f710030 100644 --- a/docs/data-sources/projects.md +++ b/docs/data-sources/projects.md @@ -197,3 +197,5 @@ Read-Only: - `deployment_action` (String) - `package_reference` (String) + + diff --git a/docs/data-sources/script_modules.md b/docs/data-sources/script_modules.md index 8a4ab70c5..283f804cc 100644 --- a/docs/data-sources/script_modules.md +++ b/docs/data-sources/script_modules.md @@ -56,3 +56,5 @@ Read-Only: - `body` (String) - `syntax` (String) + + diff --git a/docs/data-sources/space.md b/docs/data-sources/space.md index 70897c61f..36bcc4c81 100644 --- a/docs/data-sources/space.md +++ b/docs/data-sources/space.md @@ -28,3 +28,5 @@ Provides information about an existing space. - `slug` (String) The unique slug of this space. - `space_managers_team_members` (Set of String) A list of user IDs designated to be managers of this space. - `space_managers_teams` (Set of String) A list of team IDs designated to be managers of this space. + + diff --git a/docs/data-sources/spaces.md b/docs/data-sources/spaces.md index 3cda99d0b..36485b0a1 100644 --- a/docs/data-sources/spaces.md +++ b/docs/data-sources/spaces.md @@ -49,3 +49,5 @@ Read-Only: - `slug` (String) The unique slug of this space. - `space_managers_team_members` (Set of String) A list of user IDs designated to be managers of this space. - `space_managers_teams` (Set of String) A list of team IDs designated to be managers of this space. + + diff --git a/docs/data-sources/ssh_connection_deployment_targets.md b/docs/data-sources/ssh_connection_deployment_targets.md index c9918d5b2..bf9a5d85f 100644 --- a/docs/data-sources/ssh_connection_deployment_targets.md +++ b/docs/data-sources/ssh_connection_deployment_targets.md @@ -171,3 +171,5 @@ Read-Only: - `upgrade_required` (Boolean) - `upgrade_suggested` (Boolean) - `version` (String) + + diff --git a/docs/data-sources/tag_sets.md b/docs/data-sources/tag_sets.md index 8061cfb1c..fb8fffe78 100644 --- a/docs/data-sources/tag_sets.md +++ b/docs/data-sources/tag_sets.md @@ -38,3 +38,5 @@ Read-Only: - `name` (String) The name of this resource. - `sort_order` (Number) The sort order associated with this resource. - `space_id` (String) The space ID associated with this resource. + + diff --git a/docs/data-sources/teams.md b/docs/data-sources/teams.md index 180ed6f56..ba635c2e0 100644 --- a/docs/data-sources/teams.md +++ b/docs/data-sources/teams.md @@ -53,3 +53,5 @@ Read-Only: - `display_id_and_name` (Boolean) - `display_name` (String) - `id` (String) + + diff --git a/docs/data-sources/tenants.md b/docs/data-sources/tenants.md index 4941b440b..46a4cf5aa 100644 --- a/docs/data-sources/tenants.md +++ b/docs/data-sources/tenants.md @@ -53,3 +53,5 @@ Read-Only: - `environments` (List of String) - `project_id` (String) + + diff --git a/docs/data-sources/user_roles.md b/docs/data-sources/user_roles.md index 0757d6ce6..19010c518 100644 --- a/docs/data-sources/user_roles.md +++ b/docs/data-sources/user_roles.md @@ -51,3 +51,5 @@ Read-Only: - `space_permission_descriptions` (List of String) - `supported_restrictions` (List of String) - `system_permission_descriptions` (List of String) + + diff --git a/docs/data-sources/users.md b/docs/data-sources/users.md index a0be264d4..dd1a19994 100644 --- a/docs/data-sources/users.md +++ b/docs/data-sources/users.md @@ -68,3 +68,5 @@ Read-Only: - `is_identifying_claim` (Boolean) - `name` (String) - `value` (String) + + diff --git a/docs/data-sources/variables.md b/docs/data-sources/variables.md index 1fd0a1563..51d026e09 100644 --- a/docs/data-sources/variables.md +++ b/docs/data-sources/variables.md @@ -80,3 +80,5 @@ Read-Only: - `display_name` (String) - `value` (String) + + diff --git a/docs/data-sources/worker_pools.md b/docs/data-sources/worker_pools.md index e23a062b7..7c6636313 100644 --- a/docs/data-sources/worker_pools.md +++ b/docs/data-sources/worker_pools.md @@ -43,3 +43,5 @@ Read-Only: - `space_id` (String) The space ID associated with this resource. - `worker_pool_type` (String) - `worker_type` (String) + + diff --git a/docs/resources/aws_openid_connect_account.md b/docs/resources/aws_openid_connect_account.md new file mode 100644 index 000000000..03301f13f --- /dev/null +++ b/docs/resources/aws_openid_connect_account.md @@ -0,0 +1,40 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "octopusdeploy_aws_openid_connect_account Resource - terraform-provider-octopusdeploy" +subcategory: "" +description: |- + This resource manages AWS OIDC accounts in Octopus Deploy. +--- + +# octopusdeploy_aws_openid_connect_account (Resource) + +This resource manages AWS OIDC accounts in Octopus Deploy. + + + + +## Schema + +### Required + +- `name` (String) The name of this AWS OIDC account. +- `role_arn` (String) The Amazon Resource Name (ARN) of the role that the caller is assuming. + +### Optional + +- `account_test_subject_keys` (List of String) Keys to include in an account test. Valid options are: `space`, `account`, `type` +- `description` (String) A user-friendly description of this AWS OIDC account. +- `environments` (List of String) A list of environment IDs associated with this resource. +- `execution_subject_keys` (List of String) Keys to include in a deployment or runbook. Valid options are `space`, `environment`, `project`, `tenant`, `runbook`, `account`, `type` +- `health_subject_keys` (List of String) Keys to include in a health check. Valid options are `space`, `account`, `target`, `type` +- `session_duration` (Number) The duration, in seconds, of the role session. +- `space_id` (String) The space ID associated with this resource. +- `tenant_tags` (List of String) A list of tenant tags associated with this resource. +- `tenanted_deployment_participation` (String) The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`. +- `tenants` (List of String) A list of tenant IDs associated with this resource. + +### Read-Only + +- `id` (String) The ID of this resource. + + diff --git a/docs/resources/external_feed_create_release_trigger.md b/docs/resources/external_feed_create_release_trigger.md index c4c6e2bf0..e6b02d6c1 100644 --- a/docs/resources/external_feed_create_release_trigger.md +++ b/docs/resources/external_feed_create_release_trigger.md @@ -1,5 +1,4 @@ --- -# generated by https://github.com/hashicorp/terraform-plugin-docs page_title: "octopusdeploy_external_feed_create_release_trigger Resource - terraform-provider-octopusdeploy" subcategory: "Triggers" description: |- @@ -8,13 +7,14 @@ description: |- # octopusdeploy_external_feed_create_release_trigger (Resource) +This resource manages External feed triggers (release creation type) in Octopus Deploy. ## Example Usage ```terraform resource "octopusdeploy_external_feed_create_release_trigger" "my_trigger" { name = "My feed trigger" - space_id = "Spaces-1 + space_id = "Spaces-1" project_id = "Projects-2" package { deployment_action = "My Helm step" @@ -27,8 +27,6 @@ resource "octopusdeploy_external_feed_create_release_trigger" "my_trigger" { channel_id = "Channels-21" } ``` - - ## Schema @@ -55,3 +53,11 @@ Optional: - `deployment_action` (String) - `package_reference` (String) + +## Import + +Import is supported using the following syntax: + +```shell +terraform import [options] octopusdeploy_external_feed_create_release_trigger. +``` diff --git a/docs/resources/git_credential.md b/docs/resources/git_credential.md index 57f2a6b00..e47402fa1 100644 --- a/docs/resources/git_credential.md +++ b/docs/resources/git_credential.md @@ -27,3 +27,5 @@ This resource manages Git credentials in Octopus Deploy. - `id` (String) The unique ID for this resource. - `space_id` (String) The space ID associated with this resource. - `type` (String) The Git credential authentication type. + + diff --git a/docs/resources/library_variable_set.md b/docs/resources/library_variable_set.md index 69a2c2300..5cdf3b26a 100644 --- a/docs/resources/library_variable_set.md +++ b/docs/resources/library_variable_set.md @@ -44,3 +44,5 @@ Optional: - `help_text` (String) The help presented alongside the parameter input. - `id` (String) The unique ID for this resource. - `label` (String) The label shown beside the parameter when presented in the deployment process. Example: `Server name`. + + diff --git a/docs/resources/machine_policy.md b/docs/resources/machine_policy.md index ad2e0203f..b49054e28 100644 --- a/docs/resources/machine_policy.md +++ b/docs/resources/machine_policy.md @@ -97,3 +97,5 @@ Optional: - `calamari_update_behavior` (String) - `tentacle_update_account_id` (String) - `tentacle_update_behavior` (String) + + diff --git a/docs/resources/project_scheduled_trigger.md b/docs/resources/project_scheduled_trigger.md new file mode 100644 index 000000000..a6a8a56b0 --- /dev/null +++ b/docs/resources/project_scheduled_trigger.md @@ -0,0 +1,201 @@ +--- +page_title: "octopusdeploy_project_scheduled_trigger Resource - terraform-provider-octopusdeploy" +subcategory: "Triggers" +description: |- + This resource manages a scheduled trigger for a project or runbook in Octopus Deploy. +--- + +# octopusdeploy_project_scheduled_trigger (Resource) + +This resource manages a scheduled trigger for a project or runbook in Octopus Deploy. + +## Example Usage + +```terraform +resource "octopusdeploy_project_scheduled_trigger" "once_daily_example" { + name = "Once Daily example" + description = "This is a once daily schedule" + project_id = "projects-123" + space_id = "spaces-123" + deploy_new_release_action { + destination_environment_id = "environments-123" + } + once_daily_schedule { + start_time = "2024-03-22T09:00:00" + days_of_week = ["Tuesday", "Wednesday", "Monday"] + } +} + +resource "octopusdeploy_project_scheduled_trigger" "continuous_example" { + name = "Continuous" + description = "This is a continuous daily schedule" + project_id = "projects-123" + space_id = "spaces-123" + deploy_new_release_action { + destination_environment_id = "environments-123" + } + continuous_daily_schedule { + interval = "OnceHourly" + hour_interval = 3 + run_after = "2024-03-22T09:00:00" + run_until = "2024-03-29T13:00:00" + days_of_week = ["Monday", "Tuesday", "Friday"] + } +} + +resource "octopusdeploy_project_scheduled_trigger" "deploy_latest_example" { + name = "Deploy Latest" + project_id = "projects-123" + space_id = "spaces-123" + deploy_latest_release_action { + source_environment_id = "environments-321" + destination_environment_id = "environments-123" + should_redeploy = true + } + cron_expression_schedule { + cron_expression = "0 0 06 * * Mon-Fri" + } +} + +resource "octopusdeploy_project_scheduled_trigger" "deploy_new_example" { + name = "Deploy New" + project_id = "projects-123" + space_id = "spaces-123" + deploy_new_release_action { + destination_environment_id = "environments-123" + } + cron_expression_schedule { + cron_expression = "0 0 06 * * Mon-Fri" + } +} + +resource "octopusdeploy_project_scheduled_trigger" "runbook_example" { + name = "Runbook" + description = "This is a Cron schedule" + project_id = "projects-123" + space_id = "spaces-123" + run_runbook_action { + target_environment_ids = ["environments-123", "environments-321"] + runbook_id = "runbooks-123" + } + cron_expression_schedule { + cron_expression = "0 0 06 * * Mon-Fri" + } +} +``` + +## Schema + +### Required + +- `name` (String) The name of this resource. +- `project_id` (String) The ID of the project to attach the trigger. +- `space_id` (String) The space ID where this trigger's project exists. + +### Optional + +- `channel_id` (String) The channel ID to use when creating the release. Will use the default channel if left blank. +- `continuous_daily_schedule` (Block Set, Max: 1) The daily schedule for the trigger. (see [below for nested schema](#nestedblock--continuous_daily_schedule)) +- `cron_expression_schedule` (Block Set, Max: 1) The cron expression schedule for the trigger. (see [below for nested schema](#nestedblock--cron_expression_schedule)) +- `days_per_month_schedule` (Block Set, Max: 1) The daily schedule for the trigger. (see [below for nested schema](#nestedblock--days_per_month_schedule)) +- `deploy_latest_release_action` (Block Set, Max: 1) Configuration for deploying the latest release. Can not be used with 'deploy_new_release_action' or 'run_runbook_action'. (see [below for nested schema](#nestedblock--deploy_latest_release_action)) +- `deploy_new_release_action` (Block Set, Max: 1) Configuration for deploying a new release. Can not be used with 'deploy_latest_release_action' or 'run_runbook_action'. (see [below for nested schema](#nestedblock--deploy_new_release_action)) +- `description` (String) A description of the trigger. +- `is_disabled` (Boolean) Indicates whether the trigger is disabled. +- `once_daily_schedule` (Block Set, Max: 1) The daily schedule for the trigger. (see [below for nested schema](#nestedblock--once_daily_schedule)) +- `run_runbook_action` (Block Set, Max: 1) Configuration for running a runbook. Can not be used with 'deploy_latest_release_action' or 'deploy_new_release_action'. (see [below for nested schema](#nestedblock--run_runbook_action)) +- `tenant_ids` (List of String) The IDs of the tenants to deploy to. +- `timezone` (String) The timezone for the trigger. + +### Read-Only + +- `id` (String) The ID of this resource. + + +### Nested Schema for `continuous_daily_schedule` + +Required: + +- `days_of_week` (List of String) The days of the week to run the trigger. +- `interval` (String) The interval in minutes to run the trigger. +- `run_after` (String) The time of day to start the trigger. +- `run_until` (String) The time of day to end the trigger. + +Optional: + +- `hour_interval` (Number) How often to run the trigger in hours. Only used when the interval is set to 'OnceHourly'. +- `minute_interval` (Number) How often to run the trigger in minutes. Only used when the interval is set to 'OnceEveryMinute'. + + + +### Nested Schema for `cron_expression_schedule` + +Required: + +- `cron_expression` (String) The cron expression for the schedule. + + + +### Nested Schema for `days_per_month_schedule` + +Required: + +- `monthly_schedule_type` (String) The type of monthly schedule to run the trigger +- `start_time` (String) The time of day to start the trigger. + +Optional: + +- `date_of_month` (String) Which date of the month to run the trigger. String number between 1 - 31 Incl. or L for the last day of the month. +- `day_number_of_month` (String) Which ordinal day of the week to run the trigger on. String number between 1 - 4 Incl. or L for the last occurrence of day_of_week for the month. +- `day_of_week` (String) Which day of the week to run the trigger on. Required when monthly_schedule_type is set to 'DayOfMonth'. + + + +### Nested Schema for `deploy_latest_release_action` + +Required: + +- `destination_environment_id` (String) The environment ID to deploy the selected release to. +- `source_environment_id` (String) The environment ID to use when selecting the release to deploy from. + +Optional: + +- `should_redeploy` (Boolean) Enable to re-deploy to the deployment targets even if they are already up-to-date with the current deployment. + + + +### Nested Schema for `deploy_new_release_action` + +Required: + +- `destination_environment_id` (String) The environment ID to deploy the selected release to. + +Optional: + +- `git_reference` (String) The git reference to use when creating the release. Can be a branch, tag, or commit hash. + + + +### Nested Schema for `once_daily_schedule` + +Required: + +- `days_of_week` (List of String) The days of the week to run the trigger. +- `start_time` (String) The time of day to start the trigger. + + + +### Nested Schema for `run_runbook_action` + +Required: + +- `runbook_id` (String) The ID of the runbook to run. +- `target_environment_ids` (List of String) The IDs of the environments to run the runbook in. + +## Import + +Import is supported using the following syntax: + +```shell +terraform import [options] octopusdeploy_project_scheduled_trigger. +``` diff --git a/docs/resources/runbook.md b/docs/resources/runbook.md index 4a96a9fec..b540fd6e7 100644 --- a/docs/resources/runbook.md +++ b/docs/resources/runbook.md @@ -56,3 +56,5 @@ Optional: - `quantity_to_keep` (Number) How many runs to keep per environment. - `should_keep_forever` (Boolean) Indicates if items should never be deleted. The default value is `false`. + + diff --git a/docs/resources/script_module.md b/docs/resources/script_module.md index a9f4357f1..15d81fb84 100644 --- a/docs/resources/script_module.md +++ b/docs/resources/script_module.md @@ -24,41 +24,6 @@ resource "octopusdeploy_script_module" "example" { } ``` -To include the script module in a project: -```terraform -resource "octopusdeploy_project" "example-project" { - auto_create_release = false - default_guided_failure_mode = "EnvironmentDefault" - default_to_skip_if_already_installed = false - description = "The development project." - discrete_channel_release = false - is_disabled = false - is_discrete_channel_release = false - is_version_controlled = false - lifecycle_id = "Lifecycles-342" - name = "Development Project (OK to Delete)" - project_group_id = "ProjectGroups-342" - tenanted_deployment_participation = "TenantedOrUntenanted" - included_library_variable_sets = [octopusdeploy_script_module.example.id] - - connectivity_policy { - allow_deployments_to_no_targets = false - exclude_unhealthy_targets = false - skip_machine_behavior = "SkipUnavailableMachines" - } - - template { - default_value = "example-default-value" - help_text = "example-help-test" - label = "example-label" - name = "example-template-value" - display_settings = { - "Octopus.ControlType" : "SingleLineText" - } - } -} -``` - ## Schema diff --git a/docs/resources/tag.md b/docs/resources/tag.md index d20232cc6..9f40e39d8 100644 --- a/docs/resources/tag.md +++ b/docs/resources/tag.md @@ -31,3 +31,5 @@ This resource manages tags in Octopus Deploy. - `canonical_tag_name` (String) - `id` (String) The ID of this resource. + + diff --git a/docs/resources/tenant.md b/docs/resources/tenant.md index 7e2aaa006..60a2793f8 100644 --- a/docs/resources/tenant.md +++ b/docs/resources/tenant.md @@ -35,3 +35,5 @@ Required: - `environments` (List of String) A list of environment IDs associated with this tenant through a project. - `project_id` (String) The project ID associated with this tenant. + + diff --git a/docs/resources/tenant_common_variable.md b/docs/resources/tenant_common_variable.md index c4a8220eb..4c1c54054 100644 --- a/docs/resources/tenant_common_variable.md +++ b/docs/resources/tenant_common_variable.md @@ -29,3 +29,5 @@ This resource manages tenant common variables in Octopus Deploy. ### Read-Only - `id` (String) The ID of this resource. + + diff --git a/docs/resources/tenant_project_variable.md b/docs/resources/tenant_project_variable.md index 024e4e06a..c27b1afae 100644 --- a/docs/resources/tenant_project_variable.md +++ b/docs/resources/tenant_project_variable.md @@ -30,3 +30,5 @@ This resource manages tenant project variables in Octopus Deploy. ### Read-Only - `id` (String) The ID of this resource. + + diff --git a/examples/resources/octopusdeploy_external_feed_create_release_trigger/import.sh b/examples/resources/octopusdeploy_external_feed_create_release_trigger/import.sh new file mode 100644 index 000000000..4ac902242 --- /dev/null +++ b/examples/resources/octopusdeploy_external_feed_create_release_trigger/import.sh @@ -0,0 +1 @@ +terraform import [options] octopusdeploy_external_feed_create_release_trigger. \ No newline at end of file diff --git a/examples/resources/octopusdeploy_external_feed_create_release_trigger/resource.tf b/examples/resources/octopusdeploy_external_feed_create_release_trigger/resource.tf new file mode 100644 index 000000000..57fa1baed --- /dev/null +++ b/examples/resources/octopusdeploy_external_feed_create_release_trigger/resource.tf @@ -0,0 +1,14 @@ +resource "octopusdeploy_external_feed_create_release_trigger" "my_trigger" { + name = "My feed trigger" + space_id = "Spaces-1" + project_id = "Projects-2" + package { + deployment_action = "My Helm step" + package_reference = "nginx" + } + package { + deployment_action = "My container step" + package_reference = "busybox" + } + channel_id = "Channels-21" +} \ No newline at end of file diff --git a/examples/resources/octopusdeploy_project_scheduled_trigger/import.sh b/examples/resources/octopusdeploy_project_scheduled_trigger/import.sh new file mode 100644 index 000000000..89f974fba --- /dev/null +++ b/examples/resources/octopusdeploy_project_scheduled_trigger/import.sh @@ -0,0 +1 @@ +terraform import [options] octopusdeploy_project_scheduled_trigger. \ No newline at end of file diff --git a/examples/resources/octopusdeploy_project_scheduled_trigger/resource.tf b/examples/resources/octopusdeploy_project_scheduled_trigger/resource.tf new file mode 100644 index 000000000..536d29a48 --- /dev/null +++ b/examples/resources/octopusdeploy_project_scheduled_trigger/resource.tf @@ -0,0 +1,70 @@ +resource "octopusdeploy_project_scheduled_trigger" "once_daily_example" { + name = "Once Daily example" + description = "This is a once daily schedule" + project_id = "projects-123" + space_id = "spaces-123" + deploy_new_release_action { + destination_environment_id = "environments-123" + } + once_daily_schedule { + start_time = "2024-03-22T09:00:00" + days_of_week = ["Tuesday", "Wednesday", "Monday"] + } +} + +resource "octopusdeploy_project_scheduled_trigger" "continuous_example" { + name = "Continuous" + description = "This is a continuous daily schedule" + project_id = "projects-123" + space_id = "spaces-123" + deploy_new_release_action { + destination_environment_id = "environments-123" + } + continuous_daily_schedule { + interval = "OnceHourly" + hour_interval = 3 + run_after = "2024-03-22T09:00:00" + run_until = "2024-03-29T13:00:00" + days_of_week = ["Monday", "Tuesday", "Friday"] + } +} + +resource "octopusdeploy_project_scheduled_trigger" "deploy_latest_example" { + name = "Deploy Latest" + project_id = "projects-123" + space_id = "spaces-123" + deploy_latest_release_action { + source_environment_id = "environments-321" + destination_environment_id = "environments-123" + should_redeploy = true + } + cron_expression_schedule { + cron_expression = "0 0 06 * * Mon-Fri" + } +} + +resource "octopusdeploy_project_scheduled_trigger" "deploy_new_example" { + name = "Deploy New" + project_id = "projects-123" + space_id = "spaces-123" + deploy_new_release_action { + destination_environment_id = "environments-123" + } + cron_expression_schedule { + cron_expression = "0 0 06 * * Mon-Fri" + } +} + +resource "octopusdeploy_project_scheduled_trigger" "runbook_example" { + name = "Runbook" + description = "This is a Cron schedule" + project_id = "projects-123" + space_id = "spaces-123" + run_runbook_action { + target_environment_ids = ["environments-123", "environments-321"] + runbook_id = "runbooks-123" + } + cron_expression_schedule { + cron_expression = "0 0 06 * * Mon-Fri" + } +} \ No newline at end of file diff --git a/integration_test.go b/integration_test.go index 5529094d4..45f452bba 100644 --- a/integration_test.go +++ b/integration_test.go @@ -3593,3 +3593,61 @@ func TestPackageFeedCreateReleaseTriggerResources(t *testing.T) { return nil }) } + +func TestProjectScheduledTriggerResources(t *testing.T) { + testFramework := test.OctopusContainerTest{} + testFramework.ArrangeTest(t, func(t *testing.T, container *test.OctopusContainer, spaceClient *client.Client) error { + // Act + newSpaceId, err := testFramework.Act(t, container, "./terraform", "53-scheduledprojecttrigger", []string{}) + + // Assert + client, err := octoclient.CreateClient(container.URI, newSpaceId, test.ApiKey) + query := projects.ProjectsQuery{ + Skip: 0, + Take: 2, + } + + resources, err := client.Projects.Get(query) + if err != nil { + return err + } + + if len(resources.Items) != 2 { + t.Fatal("There must be exactly 2 projects in the space") + } + + nonTenantedProjectName := "Non Tenanted" + nonTenantedProjectIndex := stdslices.IndexFunc(resources.Items, func(t *projects.Project) bool { return t.Name == nonTenantedProjectName }) + nonTenantedProject := resources.Items[nonTenantedProjectIndex] + + tenantedProjectName := "Tenanted" + tenantedProjectIndex := stdslices.IndexFunc(resources.Items, func(t *projects.Project) bool { return t.Name == tenantedProjectName }) + tenantedProject := resources.Items[tenantedProjectIndex] + + projectTriggers, err := client.ProjectTriggers.GetAll() + if err != nil { + return err + } + + nonTenantedProjectTriggersCount := 0 + tenantedProjectTriggersCount := 0 + + for _, trigger := range projectTriggers { + if trigger.ProjectID == nonTenantedProject.ID { + nonTenantedProjectTriggersCount++ + } else if trigger.ProjectID == tenantedProject.ID { + tenantedProjectTriggersCount++ + } + } + + if nonTenantedProjectTriggersCount != 9 { + t.Fatal("Non Tenanted project should have exactly 8 project triggers and 1 runbook trigger, only found: " + fmt.Sprint(nonTenantedProjectTriggersCount)) + } + + if tenantedProjectTriggersCount != 2 { + t.Fatal("Tenanted project should have exactly 1 project trigger and 1 runbook trigger, only found: " + fmt.Sprint(tenantedProjectTriggersCount)) + } + + return nil + }) +} diff --git a/octopusdeploy/provider.go b/octopusdeploy/provider.go index 64ae23dde..8fb34b2bf 100644 --- a/octopusdeploy/provider.go +++ b/octopusdeploy/provider.go @@ -77,6 +77,7 @@ func Provider() *schema.Provider { "octopusdeploy_project": resourceProject(), "octopusdeploy_project_deployment_target_trigger": resourceProjectDeploymentTargetTrigger(), "octopusdeploy_external_feed_create_release_trigger": resourceExternalFeedCreateReleaseTrigger(), + "octopusdeploy_project_scheduled_trigger": resourceProjectScheduledTrigger(), "octopusdeploy_project_group": resourceProjectGroup(), "octopusdeploy_runbook": resourceRunbook(), "octopusdeploy_runbook_process": resourceRunbookProcess(), diff --git a/octopusdeploy/resource_external_feed_create_release_trigger.go b/octopusdeploy/resource_external_feed_create_release_trigger.go index eadc1620a..28e01c6a4 100644 --- a/octopusdeploy/resource_external_feed_create_release_trigger.go +++ b/octopusdeploy/resource_external_feed_create_release_trigger.go @@ -17,6 +17,7 @@ func resourceExternalFeedCreateReleaseTrigger() *schema.Resource { return &schema.Resource{ CreateContext: resourceExternalFeedCreateReleaseTriggerCreate, DeleteContext: resourceExternalFeedCreateReleaseTriggerDelete, + Description: "This resource manages External feed triggers (release creation type) in Octopus Deploy.", Importer: getImporter(), ReadContext: resourceExternalFeedCreateReleaseTriggerRead, Schema: getExternalFeedCreateReleaseTriggerSchema(), diff --git a/octopusdeploy/resource_project_scheduled_trigger.go b/octopusdeploy/resource_project_scheduled_trigger.go new file mode 100644 index 000000000..272758c11 --- /dev/null +++ b/octopusdeploy/resource_project_scheduled_trigger.go @@ -0,0 +1,119 @@ +package octopusdeploy + +import ( + "context" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "log" +) + +func resourceProjectScheduledTrigger() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceProjectScheduledTriggerCreate, + DeleteContext: resourceProjectScheduledTriggerDelete, + Description: "This resource manages a scheduled trigger for a project or runbook in Octopus Deploy.", + Importer: getImporter(), + ReadContext: resourceProjectScheduledTriggerRead, + Schema: getProjectScheduledTriggerSchema(), + UpdateContext: resourceProjectScheduledTriggerUpdate, + } +} + +func resourceProjectScheduledTriggerRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + client := m.(*client.Client) + + scheduledTrigger, err := client.ProjectTriggers.GetByID(d.Id()) + + if scheduledTrigger == nil { + d.SetId("") + if err != nil { + return diag.FromErr(err) + } + + return nil + } + + flattenedScheduledTrigger := flattenProjectScheduledTrigger(scheduledTrigger) + for key, value := range flattenedScheduledTrigger { + err := d.Set(key, value) + if err != nil { + return nil + } + } + + return nil +} + +func resourceProjectScheduledTriggerCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + client := m.(*client.Client) + projectId := d.Get("project_id").(string) + spaceId := d.Get("space_id").(string) + project, err := projects.GetByID(client, spaceId, projectId) + if err != nil { + return diag.FromErr(err) + } + + expandedScheduledTrigger, err := expandProjectScheduledTrigger(d, project) + + if err != nil { + return diag.FromErr(err) + } + + scheduledTrigger, err := client.ProjectTriggers.Add(expandedScheduledTrigger) + + if err != nil { + return diag.FromErr(err) + } + + if isEmpty(scheduledTrigger.GetID()) { + log.Println("ID is nil") + } else { + d.SetId(scheduledTrigger.GetID()) + } + + return nil +} + +func resourceProjectScheduledTriggerUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + client := m.(*client.Client) + projectId := d.Get("project_id").(string) + spaceId := d.Get("space_id").(string) + project, err := projects.GetByID(client, spaceId, projectId) + if err != nil { + return diag.FromErr(err) + } + + expandedScheduledTrigger, err := expandProjectScheduledTrigger(d, project) + + if err != nil { + return diag.FromErr(err) + } + + expandedScheduledTrigger.ID = d.Id() + + if err != nil { + return diag.FromErr(err) + } + + scheduledTrigger, err := client.ProjectTriggers.Update(expandedScheduledTrigger) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(scheduledTrigger.GetID()) + + return nil +} + +func resourceProjectScheduledTriggerDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + client := m.(*client.Client) + err := client.ProjectTriggers.DeleteByID(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + d.SetId("") + return nil +} diff --git a/octopusdeploy/schema_project_scheduled_trigger.go b/octopusdeploy/schema_project_scheduled_trigger.go new file mode 100644 index 000000000..ae773a183 --- /dev/null +++ b/octopusdeploy/schema_project_scheduled_trigger.go @@ -0,0 +1,534 @@ +package octopusdeploy + +import ( + "errors" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/actions" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/filters" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/triggers" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "time" +) + +func flattenProjectScheduledTrigger(projectScheduledTrigger *triggers.ProjectTrigger) map[string]interface{} { + flattenedProjectScheduledTrigger := map[string]interface{}{} + flattenedProjectScheduledTrigger["id"] = projectScheduledTrigger.GetID() + flattenedProjectScheduledTrigger["name"] = projectScheduledTrigger.Name + flattenedProjectScheduledTrigger["description"] = projectScheduledTrigger.Description + flattenedProjectScheduledTrigger["project_id"] = projectScheduledTrigger.ProjectID + flattenedProjectScheduledTrigger["space_id"] = projectScheduledTrigger.SpaceID + flattenedProjectScheduledTrigger["is_disabled"] = projectScheduledTrigger.IsDisabled + + actionType := projectScheduledTrigger.Action.GetActionType() + if actionType == actions.DeployLatestRelease { + deployLatestReleaseAction := projectScheduledTrigger.Action.(*actions.DeployLatestReleaseAction) + flattenedProjectScheduledTrigger["tenant_ids"] = deployLatestReleaseAction.Tenants + flattenedProjectScheduledTrigger["deploy_latest_release_action"] = []map[string]interface{}{ + { + "source_environment_id": deployLatestReleaseAction.SourceEnvironments[0], + "destination_environment_id": deployLatestReleaseAction.DestinationEnvironment, + "should_redeploy": deployLatestReleaseAction.ShouldRedeploy, + }, + } + + } else if actionType == actions.DeployNewRelease { + deployNewReleaseAction := projectScheduledTrigger.Action.(*actions.DeployNewReleaseAction) + flattenedProjectScheduledTrigger["tenant_ids"] = deployNewReleaseAction.Tenants + flattenedProjectScheduledTrigger["deploy_new_release_action"] = []map[string]interface{}{ + { + "destination_environment_id": deployNewReleaseAction.Environment, + "git_reference": deployNewReleaseAction.VersionControlReference.GitRef, + }, + } + } else if actionType == actions.RunRunbook { + runRunbookAction := projectScheduledTrigger.Action.(*actions.RunRunbookAction) + flattenedProjectScheduledTrigger["tenant_ids"] = runRunbookAction.Tenants + flattenedProjectScheduledTrigger["run_runbook_action"] = []map[string]interface{}{ + { + "runbook_id": runRunbookAction.Runbook, + "target_environment_ids": runRunbookAction.Environments, + }, + } + + } + + filterType := projectScheduledTrigger.Filter.GetFilterType() + if filterType == filters.OnceDailySchedule { + onceDailyScheduleFilter := projectScheduledTrigger.Filter.(*filters.OnceDailyScheduledTriggerFilter) + flattenedProjectScheduledTrigger["once_daily_schedule"] = []map[string]interface{}{ + { + "start_time": onceDailyScheduleFilter.Start.Format(filters.RFC3339NanoNoZone), + "days_of_week": onceDailyScheduleFilter.Days, + }, + } + flattenedProjectScheduledTrigger["timezone"] = onceDailyScheduleFilter.TimeZone + } else if filterType == filters.ContinuousDailySchedule { + continuousDailyScheduleFilter := projectScheduledTrigger.Filter.(*filters.ContinuousDailyScheduledTriggerFilter) + + flattenedProjectScheduledTrigger["continuous_daily_schedule"] = []map[string]interface{}{ + { + "interval": continuousDailyScheduleFilter.Interval.String(), + "hour_interval": continuousDailyScheduleFilter.HourInterval, + "minute_interval": continuousDailyScheduleFilter.MinuteInterval, + "run_after": continuousDailyScheduleFilter.RunAfter.Format(filters.RFC3339NanoNoZone), + "run_until": continuousDailyScheduleFilter.RunUntil.Format(filters.RFC3339NanoNoZone), + "days_of_week": continuousDailyScheduleFilter.Days, + }, + } + flattenedProjectScheduledTrigger["timezone"] = continuousDailyScheduleFilter.TimeZone + } else if filterType == filters.DaysPerMonthSchedule { + daysPerMonthScheduleFilter := projectScheduledTrigger.Filter.(*filters.DaysPerMonthScheduledTriggerFilter) + flattenedProjectScheduledTrigger["days_per_month_schedule"] = []map[string]interface{}{ + { + "start_time": daysPerMonthScheduleFilter.Start.Format(filters.RFC3339NanoNoZone), + "monthly_schedule_type": daysPerMonthScheduleFilter.MonthlySchedule.String(), + "date_of_month": daysPerMonthScheduleFilter.DateOfMonth, + "day_number_of_month": daysPerMonthScheduleFilter.DayNumberOfMonth, + "day_of_week": filters.Weekday.String(*daysPerMonthScheduleFilter.Day), + }, + } + flattenedProjectScheduledTrigger["timezone"] = daysPerMonthScheduleFilter.TimeZone + } else if filterType == filters.CronExpressionSchedule { + cronExpressionScheduleFilter := projectScheduledTrigger.Filter.(*filters.CronScheduledTriggerFilter) + flattenedProjectScheduledTrigger["cron_expression_schedule"] = []map[string]interface{}{ + { + "cron_expression": cronExpressionScheduleFilter.CronExpression, + }, + } + flattenedProjectScheduledTrigger["timezone"] = cronExpressionScheduleFilter.TimeZone + } + + return flattenedProjectScheduledTrigger +} + +func expandProjectScheduledTrigger(projectScheduledTrigger *schema.ResourceData, project *projects.Project) (*triggers.ProjectTrigger, error) { + name := projectScheduledTrigger.Get("name").(string) + description := projectScheduledTrigger.Get("description").(string) + isDisabled := projectScheduledTrigger.Get("is_disabled").(bool) + timezone := projectScheduledTrigger.Get("timezone").(string) + + var action actions.ITriggerAction = nil + var filter filters.ITriggerFilter = nil + + // Action configuration + if attr, ok := projectScheduledTrigger.GetOk("deploy_latest_release_action"); ok { + deployLatestReleaseActionList := attr.(*schema.Set).List() + deployLatestReleaseActionMap := deployLatestReleaseActionList[0].(map[string]interface{}) + deploymentAction := actions.NewDeployLatestReleaseAction( + deployLatestReleaseActionMap["destination_environment_id"].(string), + deployLatestReleaseActionMap["should_redeploy"].(bool), + []string{deployLatestReleaseActionMap["source_environment_id"].(string)}, + "", + ) + + deploymentAction.Channel = projectScheduledTrigger.Get("channel_id").(string) + deploymentAction.Tenants = expandArray(projectScheduledTrigger.Get("tenant_ids").([]interface{})) + + action = deploymentAction + } + + if attr, ok := projectScheduledTrigger.GetOk("deploy_new_release_action"); ok { + deployNewReleaseActionList := attr.(*schema.Set).List() + deployNewReleaseActionMap := deployNewReleaseActionList[0].(map[string]interface{}) + deploymentAction := actions.NewDeployNewReleaseAction( + deployNewReleaseActionMap["destination_environment_id"].(string), + "", + &actions.VersionControlReference{GitRef: deployNewReleaseActionMap["git_reference"].(string)}, + ) + + deploymentAction.Channel = projectScheduledTrigger.Get("channel_id").(string) + deploymentAction.Tenants = expandArray(projectScheduledTrigger.Get("tenant_ids").([]interface{})) + action = deploymentAction + } + + if attr, ok := projectScheduledTrigger.GetOk("run_runbook_action"); ok { + runRunbookActionList := attr.(*schema.Set).List() + runRunbookActionMap := runRunbookActionList[0].(map[string]interface{}) + deploymentAction := actions.NewRunRunbookAction() + + deploymentAction.Runbook = runRunbookActionMap["runbook_id"].(string) + deploymentAction.Environments = expandArray(runRunbookActionMap["target_environment_ids"].([]interface{})) + deploymentAction.Tenants = expandArray(projectScheduledTrigger.Get("tenant_ids").([]interface{})) + action = deploymentAction + } + + // Filter configuration + if attr, ok := projectScheduledTrigger.GetOk("once_daily_schedule"); ok { + if filter != nil { + return nil, errors.New("only one of 'once_daily_schedule', 'continuous_daily_schedule', 'days_per_month_schedule', or 'cron_expression_schedule' can be set") + } + + onceDailyScheduleFilterList := attr.(*schema.Set).List() + onceDailyScheduleFilterMap := onceDailyScheduleFilterList[0].(map[string]interface{}) + + startTime, err := time.Parse(filters.RFC3339NanoNoZone, onceDailyScheduleFilterMap["start_time"].(string)) + if err != nil { + return nil, err + } + + days := expandArray(onceDailyScheduleFilterMap["days_of_week"].([]interface{})) + + parsedDays := make([]filters.Weekday, len(days)) + for i := range days { + parsedDays[i], _ = filters.WeekdayString(days[i]) + } + + onceDailyScheduleFilter := filters.NewOnceDailyScheduledTriggerFilter(parsedDays, startTime) + onceDailyScheduleFilter.TimeZone = timezone + filter = onceDailyScheduleFilter + } + + if attr, ok := projectScheduledTrigger.GetOk("continuous_daily_schedule"); ok { + if filter != nil { + return nil, errors.New("only one of 'once_daily_schedule', 'continuous_daily_schedule', 'days_per_month_schedule', or 'cron_expression_schedule' can be set") + } + + continuousDailyScheduleFilterList := attr.(*schema.Set).List() + continuousDailyScheduleFilterMap := continuousDailyScheduleFilterList[0].(map[string]interface{}) + + interval, _ := filters.DailyScheduledIntervalString(continuousDailyScheduleFilterMap["interval"].(string)) + runAfter, err := time.Parse(filters.RFC3339NanoNoZone, continuousDailyScheduleFilterMap["run_after"].(string)) + if err != nil { + return nil, err + } + + runUntil, err := time.Parse(filters.RFC3339NanoNoZone, continuousDailyScheduleFilterMap["run_until"].(string)) + if err != nil { + return nil, err + } + + days := expandArray(continuousDailyScheduleFilterMap["days_of_week"].([]interface{})) + parsedDays := make([]filters.Weekday, len(days)) + for i := range days { + parsedDays[i], _ = filters.WeekdayString(days[i]) + } + + continuousDailyScheduleFilter := filters.NewContinuousDailyScheduledTriggerFilter(parsedDays, timezone) + continuousDailyScheduleFilter.Interval = &interval + continuousDailyScheduleFilter.RunAfter = &runAfter + continuousDailyScheduleFilter.RunUntil = &runUntil + + if interval == filters.OnceHourly { + hourInterval := int16(continuousDailyScheduleFilterMap["hour_interval"].(int)) + continuousDailyScheduleFilter.HourInterval = &hourInterval + } else if interval == filters.OnceEveryMinute { + minuteInterval := int16(continuousDailyScheduleFilterMap["minute_interval"].(int)) + continuousDailyScheduleFilter.MinuteInterval = &minuteInterval + } + + filter = continuousDailyScheduleFilter + } + + if attr, ok := projectScheduledTrigger.GetOk("days_per_month_schedule"); ok { + if filter != nil { + return nil, errors.New("only one of 'once_daily_schedule', 'continuous_daily_schedule', 'days_per_month_schedule', or 'cron_expression_schedule' can be set") + } + + daysPerMonthScheduleFilterList := attr.(*schema.Set).List() + daysPerMonthScheduleFilterMap := daysPerMonthScheduleFilterList[0].(map[string]interface{}) + + startTime, _ := time.Parse(filters.RFC3339NanoNoZone, daysPerMonthScheduleFilterMap["start_time"].(string)) + monthlyScheduleType, _ := filters.MonthlyScheduleString(daysPerMonthScheduleFilterMap["monthly_schedule_type"].(string)) + + daysPerMonthScheduleFilter := filters.NewDaysPerMonthScheduledTriggerFilter(monthlyScheduleType, startTime) + + daysPerMonthScheduleFilter.DateOfMonth = daysPerMonthScheduleFilterMap["date_of_month"].(string) + daysPerMonthScheduleFilter.DayNumberOfMonth = daysPerMonthScheduleFilterMap["day_number_of_month"].(string) + + dayOfWeek, _ := filters.WeekdayString(daysPerMonthScheduleFilterMap["day_of_week"].(string)) + daysPerMonthScheduleFilter.Day = &dayOfWeek + + daysPerMonthScheduleFilter.TimeZone = timezone + filter = daysPerMonthScheduleFilter + } + + if attr, ok := projectScheduledTrigger.GetOk("cron_expression_schedule"); ok { + if filter != nil { + return nil, errors.New("only one of 'once_daily_schedule', 'continuous_daily_schedule', 'days_per_month_schedule', or 'cron_expression_schedule' can be set") + } + + cronExpressionScheduleFilterList := attr.(*schema.Set).List() + cronExpressionScheduleFilterMap := cronExpressionScheduleFilterList[0].(map[string]interface{}) + + cronExpression := cronExpressionScheduleFilterMap["cron_expression"].(string) + cronExpressionScheduleFilter := filters.NewCronScheduledTriggerFilter(cronExpression, timezone) + + filter = cronExpressionScheduleFilter + } + + // NewProjectTrigger doesn't actually use the description value + projectTriggerToCreate := triggers.NewProjectTrigger(name, description, isDisabled, project, action, filter) + projectTriggerToCreate.Description = description + + return projectTriggerToCreate, nil +} + +func getProjectScheduledTriggerSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "name": getNameSchema(true), + "description": { + Description: "A description of the trigger.", + Optional: true, + Type: schema.TypeString, + }, + "project_id": { + Description: "The ID of the project to attach the trigger.", + Required: true, + Type: schema.TypeString, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringIsNotWhiteSpace), + ForceNew: true, + }, + "space_id": { + Required: true, + Description: "The space ID where this trigger's project exists.", + Type: schema.TypeString, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringIsNotWhiteSpace), + ForceNew: true, + }, + "deploy_latest_release_action": { + Description: "Configuration for deploying the latest release. Can not be used with 'deploy_new_release_action' or 'run_runbook_action'.", + Optional: true, + Type: schema.TypeSet, + Elem: &schema.Resource{Schema: getDeployLatestReleaseActionSchema()}, + ConflictsWith: []string{"deploy_new_release_action", "run_runbook_action"}, + MaxItems: 1, + }, + "deploy_new_release_action": { + Description: "Configuration for deploying a new release. Can not be used with 'deploy_latest_release_action' or 'run_runbook_action'.", + Optional: true, + Type: schema.TypeSet, + Elem: &schema.Resource{Schema: getDeployNewReleaseActionSchema()}, + ConflictsWith: []string{"deploy_latest_release_action", "run_runbook_action"}, + MaxItems: 1, + }, + "run_runbook_action": { + Description: "Configuration for running a runbook. Can not be used with 'deploy_latest_release_action' or 'deploy_new_release_action'.", + Optional: true, + Type: schema.TypeSet, + Elem: &schema.Resource{Schema: getRunRunbookActionSchema()}, + ConflictsWith: []string{"deploy_latest_release_action", "deploy_new_release_action"}, + MaxItems: 1, + }, + "channel_id": { + Description: "The channel ID to use when creating the release. Will use the default channel if left blank.", + Optional: true, + Type: schema.TypeString, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringIsNotWhiteSpace), + }, + "tenant_ids": { + Description: "The IDs of the tenants to deploy to.", + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Type: schema.TypeList, + }, + "is_disabled": { + Description: "Indicates whether the trigger is disabled.", + Optional: true, + Type: schema.TypeBool, + }, + "timezone": { + Description: "The timezone for the trigger.", + Optional: true, + Default: "UTC", + Type: schema.TypeString, + }, + "once_daily_schedule": { + Description: "The daily schedule for the trigger.", + Optional: true, + Type: schema.TypeSet, + Elem: &schema.Resource{Schema: getOnceDailyScheduleSchema()}, + MaxItems: 1, + }, + "continuous_daily_schedule": { + Description: "The daily schedule for the trigger.", + Optional: true, + Type: schema.TypeSet, + Elem: &schema.Resource{Schema: getContinuousDailyScheduleSchema()}, + MaxItems: 1, + }, + "days_per_month_schedule": { + Description: "The daily schedule for the trigger.", + Optional: true, + Type: schema.TypeSet, + Elem: &schema.Resource{Schema: getDaysPerMonthScheduleSchema()}, + MaxItems: 1, + }, + "cron_expression_schedule": { + Description: "The cron expression schedule for the trigger.", + Optional: true, + Type: schema.TypeSet, + Elem: &schema.Resource{Schema: getCronExpressionScheduleSchema()}, + MaxItems: 1, + }, + } +} + +// DeployLatestRelease +func getDeployLatestReleaseActionSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "source_environment_id": { + Required: true, + Description: "The environment ID to use when selecting the release to deploy from.", + Type: schema.TypeString, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringIsNotWhiteSpace), + }, + "destination_environment_id": { + Required: true, + Description: "The environment ID to deploy the selected release to.", + Type: schema.TypeString, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringIsNotWhiteSpace), + }, + "should_redeploy": { + Description: "Enable to re-deploy to the deployment targets even if they are already up-to-date with the current deployment.", + Optional: true, + Type: schema.TypeBool, + }, + } +} + +// DeployNewRelease +func getDeployNewReleaseActionSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "destination_environment_id": { + Required: true, + Description: "The environment ID to deploy the selected release to.", + Type: schema.TypeString, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringIsNotWhiteSpace), + }, + "git_reference": { + Optional: true, + Description: "The git reference to use when creating the release. Can be a branch, tag, or commit hash.", + Type: schema.TypeString, + }, + } +} + +func getRunRunbookActionSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "target_environment_ids": { + Required: true, + Description: "The IDs of the environments to run the runbook in.", + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "runbook_id": { + Required: true, + Description: "The ID of the runbook to run.", + Type: schema.TypeString, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringIsNotWhiteSpace), + }, + } +} + +// OnceDailySchedule +func getOnceDailyScheduleSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "start_time": { + Required: true, + Description: "The time of day to start the trigger.", + Type: schema.TypeString, + }, + "days_of_week": { + Required: true, + Description: "The days of the week to run the trigger.", + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + } +} + +// ContinuousDailySchedule +func getContinuousDailyScheduleSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "interval": { + Required: true, + Description: "The interval in minutes to run the trigger.", + Type: schema.TypeString, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{ + "OnceDaily", + "OnceHourly", + "OnceEveryMinute", + }, true)), + }, + "hour_interval": { + Optional: true, + Default: 0, + Description: "How often to run the trigger in hours. Only used when the interval is set to 'OnceHourly'.", + Type: schema.TypeInt, + ValidateDiagFunc: validation.ToDiagFunc(validation.IntAtLeast(0)), + }, + "minute_interval": { + Optional: true, + Default: 0, + Description: "How often to run the trigger in minutes. Only used when the interval is set to 'OnceEveryMinute'.", + Type: schema.TypeInt, + ValidateDiagFunc: validation.ToDiagFunc(validation.IntAtLeast(0)), + }, + "run_after": { + Required: true, + Description: "The time of day to start the trigger.", + Type: schema.TypeString, + }, + "run_until": { + Required: true, + Description: "The time of day to end the trigger.", + Type: schema.TypeString, + }, + "days_of_week": { + Required: true, + Description: "The days of the week to run the trigger.", + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + } +} + +// DaysPerMonthSchedule +func getDaysPerMonthScheduleSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "start_time": { + Required: true, + Description: "The time of day to start the trigger.", + Type: schema.TypeString, + }, + "monthly_schedule_type": { + Required: true, + Description: "The type of monthly schedule to run the trigger", + Type: schema.TypeString, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{ + "DateOfMonth", + "DayOfMonth", + }, true)), + }, + "date_of_month": { + Optional: true, + Description: "Which date of the month to run the trigger. String number between 1 - 31 Incl. or L for the last day of the month.", + Type: schema.TypeString, + }, + "day_number_of_month": { + Optional: true, + Description: "Which ordinal day of the week to run the trigger on. String number between 1 - 4 Incl. or L for the last occurrence of day_of_week for the month.", + Type: schema.TypeString, + }, + "day_of_week": { + // API defaults to Sunday which will cause this resource to have a diff if it is not set in state + Default: "Sunday", + Optional: true, + Description: "Which day of the week to run the trigger on. Required when monthly_schedule_type is set to 'DayOfMonth'.", + Type: schema.TypeString, + ValidateDiagFunc: validation.ToDiagFunc(validation.IsDayOfTheWeek(true)), + }, + } +} + +// CronExpressionSchedule +func getCronExpressionScheduleSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "cron_expression": { + Description: "The cron expression for the schedule.", + Required: true, + Type: schema.TypeString, + }, + } +} diff --git a/templates/resources/external_feed_create_release_trigger.md.tmpl b/templates/resources/external_feed_create_release_trigger.md.tmpl new file mode 100644 index 000000000..513b7aab6 --- /dev/null +++ b/templates/resources/external_feed_create_release_trigger.md.tmpl @@ -0,0 +1,23 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "Triggers" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile (printf "%s%s%s" "examples/resources/" .Name "/resource.tf") }} +{{ .SchemaMarkdown | trimspace }} + +{{ if .HasImport -}} +## Import + +Import is supported using the following syntax: + +{{ codefile "shell" (printf "%s%s%s" "examples/resources/" .Name "/import.sh") }} +{{- end }} diff --git a/templates/resources/project_scheduled_trigger.md.tmpl b/templates/resources/project_scheduled_trigger.md.tmpl new file mode 100644 index 000000000..513b7aab6 --- /dev/null +++ b/templates/resources/project_scheduled_trigger.md.tmpl @@ -0,0 +1,23 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "Triggers" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile (printf "%s%s%s" "examples/resources/" .Name "/resource.tf") }} +{{ .SchemaMarkdown | trimspace }} + +{{ if .HasImport -}} +## Import + +Import is supported using the following syntax: + +{{ codefile "shell" (printf "%s%s%s" "examples/resources/" .Name "/import.sh") }} +{{- end }} diff --git a/terraform/53-scheduledprojecttrigger/NonTenanted.tf b/terraform/53-scheduledprojecttrigger/NonTenanted.tf new file mode 100644 index 000000000..d5a3efaff --- /dev/null +++ b/terraform/53-scheduledprojecttrigger/NonTenanted.tf @@ -0,0 +1,219 @@ +resource "octopusdeploy_project" "non_tenanted" { + space_id = var.octopus_space_id + auto_create_release = false + default_guided_failure_mode = "EnvironmentDefault" + default_to_skip_if_already_installed = false + description = "Test project" + discrete_channel_release = false + is_disabled = false + is_discrete_channel_release = false + is_version_controlled = false + lifecycle_id = data.octopusdeploy_lifecycles.lifecycle_default_lifecycle.lifecycles[0].id + name = "Non Tenanted" + project_group_id = octopusdeploy_project_group.project_group_test.id + tenanted_deployment_participation = "Untenanted" + included_library_variable_sets = [] + versioning_strategy { + template = "#{Octopus.Version.LastMajor}.#{Octopus.Version.LastMinor}.#{Octopus.Version.LastPatch}.#{Octopus.Version.NextRevision}" + } + + connectivity_policy { + allow_deployments_to_no_targets = false + exclude_unhealthy_targets = false + skip_machine_behavior = "SkipUnavailableMachines" + } +} + + +resource "octopusdeploy_deployment_process" "non_tenanted_deployment_process" { + project_id = octopusdeploy_project.non_tenanted.id + step { + condition = "Success" + name = "Hello world (using PowerShell)" + package_requirement = "LetOctopusDecide" + start_trigger = "StartAfterPrevious" + run_script_action { + can_be_used_for_project_versioning = false + condition = "Success" + is_disabled = false + is_required = true + name = "Hello world (using PowerShell)" + script_body = <<-EOT + Write-Host 'Hello world, using PowerShell' + #TODO: Experiment with steps of your own :) + Write-Host '[Learn more about the types of steps available in Octopus](https://g.octopushq.com/OnboardingAddStepsLearnMore)' + EOT + run_on_server = true + } + } +} + +resource "octopusdeploy_runbook" "non_tenanted_runbook" { + project_id = octopusdeploy_project.non_tenanted.id + name = "Runbook" + description = "Test Runbook" + multi_tenancy_mode = "Untenanted" + connectivity_policy { + allow_deployments_to_no_targets = false + exclude_unhealthy_targets = false + skip_machine_behavior = "SkipUnavailableMachines" + } + retention_policy { + quantity_to_keep = 10 + } + environment_scope = "Specified" + environments = [octopusdeploy_environment.env_1.id, octopusdeploy_environment.env_2.id] + default_guided_failure_mode = "EnvironmentDefault" + force_package_download = true +} + +resource "octopusdeploy_runbook_process" "non_tenanted_runbook_process" { + runbook_id = octopusdeploy_runbook.non_tenanted_runbook.id + step { + condition = "Success" + name = "Hello world (using PowerShell)" + package_requirement = "LetOctopusDecide" + start_trigger = "StartAfterPrevious" + run_script_action { + can_be_used_for_project_versioning = false + condition = "Success" + is_disabled = false + is_required = true + name = "Hello world (using PowerShell)" + script_body = <<-EOT + Write-Host 'Hello world, using PowerShell' + #TODO: Experiment with steps of your own :) + Write-Host '[Learn more about the types of steps available in Octopus](https://g.octopushq.com/OnboardingAddStepsLearnMore)' + EOT + run_on_server = true + } + } +} + +resource "octopusdeploy_project_scheduled_trigger" "once_daily" { + name = "Once Daily" + description = "This is a once daily schedule" + project_id = octopusdeploy_project.non_tenanted.id + space_id = octopusdeploy_project.non_tenanted.space_id + deploy_new_release_action { + destination_environment_id = octopusdeploy_environment.env_1.id + } + once_daily_schedule { + start_time = "2024-03-22T09:00:00" + days_of_week = ["Tuesday", "Wednesday", "Monday"] + } +} + +resource "octopusdeploy_project_scheduled_trigger" "continous" { + name = "Continuous" + description = "This is a continuous daily schedule" + project_id = octopusdeploy_project.non_tenanted.id + space_id = octopusdeploy_project.non_tenanted.space_id + deploy_new_release_action { + destination_environment_id = octopusdeploy_environment.env_1.id + } + continuous_daily_schedule { + interval = "OnceHourly" + hour_interval = 3 + run_after = "2024-03-22T09:00:00" + run_until = "2024-03-29T13:00:00" + days_of_week = ["Saturday", "Monday", "Sunday", "Tuesday", "Thursday", "Friday"] + } +} + +resource "octopusdeploy_project_scheduled_trigger" "days_per_month_date_of_month" { + name = "Days Per Month" + project_id = octopusdeploy_project.non_tenanted.id + space_id = octopusdeploy_project.non_tenanted.space_id + deploy_new_release_action { + destination_environment_id = octopusdeploy_environment.env_1.id + } + days_per_month_schedule { + start_time = "2024-03-22T09:00:00" + monthly_schedule_type = "DateOfMonth" + date_of_month = "31" + } +} + + +resource "octopusdeploy_project_scheduled_trigger" "days_per_month_day_of_month" { + name = "Days Per Month Specific Day" + project_id = octopusdeploy_project.non_tenanted.id + space_id = octopusdeploy_project.non_tenanted.space_id + deploy_new_release_action { + destination_environment_id = octopusdeploy_environment.env_1.id + } + days_per_month_schedule { + start_time = "2024-03-22T09:00:00" + monthly_schedule_type = "DayOfMonth" + day_number_of_month = "L" + day_of_week = "Monday" + } +} + +resource "octopusdeploy_project_scheduled_trigger" "cron" { + name = "Cron" + description = "This is a Cron schedule" + project_id = octopusdeploy_project.non_tenanted.id + space_id = octopusdeploy_project.non_tenanted.space_id + deploy_new_release_action { + destination_environment_id = octopusdeploy_environment.env_1.id + } + cron_expression_schedule { + cron_expression = "0 0 06 * * Mon-Fri" + } +} + +resource "octopusdeploy_project_scheduled_trigger" "deploy_latest" { + name = "Cron Deploy Latest" + project_id = octopusdeploy_project.non_tenanted.id + space_id = octopusdeploy_project.non_tenanted.space_id + deploy_latest_release_action { + source_environment_id = octopusdeploy_environment.env_1.id + destination_environment_id = octopusdeploy_environment.env_2.id + should_redeploy = true + } + cron_expression_schedule { + cron_expression = "0 0 06 * * Mon-Fri" + } +} + +resource "octopusdeploy_project_scheduled_trigger" "deploy_new" { + name = "Cron Deploy New" + project_id = octopusdeploy_project.non_tenanted.id + space_id = octopusdeploy_project.non_tenanted.space_id + deploy_new_release_action { + destination_environment_id = octopusdeploy_environment.env_1.id + } + cron_expression_schedule { + cron_expression = "0 0 06 * * Mon-Fri" + } +} + + +resource "octopusdeploy_project_scheduled_trigger" "timezone" { + name = "Specific Timezone" + project_id = octopusdeploy_project.non_tenanted.id + space_id = octopusdeploy_project.non_tenanted.space_id + timezone = "Australia/Sydney" + deploy_new_release_action { + destination_environment_id = octopusdeploy_environment.env_1.id + } + cron_expression_schedule { + cron_expression = "0 0 06 * * Mon-Fri" + } +} + +resource "octopusdeploy_project_scheduled_trigger" "runbook" { + name = "Cron Runbook" + description = "This is a Cron schedule" + project_id = octopusdeploy_project.non_tenanted.id + space_id = octopusdeploy_project.non_tenanted.space_id + run_runbook_action { + target_environment_ids = [octopusdeploy_environment.env_1.id] + runbook_id = octopusdeploy_runbook.non_tenanted_runbook.id + } + cron_expression_schedule { + cron_expression = "0 0 06 * * Mon-Fri" + } +} diff --git a/terraform/53-scheduledprojecttrigger/Tenanted.tf b/terraform/53-scheduledprojecttrigger/Tenanted.tf new file mode 100644 index 000000000..601353db9 --- /dev/null +++ b/terraform/53-scheduledprojecttrigger/Tenanted.tf @@ -0,0 +1,169 @@ +resource "octopusdeploy_project" "tenanted" { + space_id = var.octopus_space_id + auto_create_release = false + default_guided_failure_mode = "EnvironmentDefault" + default_to_skip_if_already_installed = false + description = "Tenanted" + discrete_channel_release = false + is_disabled = false + is_discrete_channel_release = false + is_version_controlled = false + lifecycle_id = data.octopusdeploy_lifecycles.lifecycle_default_lifecycle.lifecycles[0].id + name = "Tenanted" + project_group_id = octopusdeploy_project_group.project_group_test.id + tenanted_deployment_participation = "Tenanted" + included_library_variable_sets = [] + versioning_strategy { + template = "#{Octopus.Version.LastMajor}.#{Octopus.Version.LastMinor}.#{Octopus.Version.LastPatch}.#{Octopus.Version.NextRevision}" + } + + connectivity_policy { + allow_deployments_to_no_targets = false + exclude_unhealthy_targets = false + skip_machine_behavior = "SkipUnavailableMachines" + } +} + + +resource "octopusdeploy_deployment_process" "tenanted_deployment_process" { + project_id = octopusdeploy_project.tenanted.id + step { + condition = "Success" + name = "Hello world (using PowerShell)" + package_requirement = "LetOctopusDecide" + start_trigger = "StartAfterPrevious" + run_script_action { + can_be_used_for_project_versioning = false + condition = "Success" + is_disabled = false + is_required = true + name = "Hello world (using PowerShell)" + script_body = <<-EOT + Write-Host 'Hello world, using PowerShell' + #TODO: Experiment with steps of your own :) + Write-Host '[Learn more about the types of steps available in Octopus](https://g.octopushq.com/OnboardingAddStepsLearnMore)' + EOT + run_on_server = true + } + } +} + +resource "octopusdeploy_runbook" "tenanted_runbook" { + project_id = octopusdeploy_project.tenanted.id + name = "Runbook" + description = "Test Runbook" + multi_tenancy_mode = "Tenanted" + connectivity_policy { + allow_deployments_to_no_targets = false + exclude_unhealthy_targets = false + skip_machine_behavior = "SkipUnavailableMachines" + } + retention_policy { + quantity_to_keep = 10 + } + environment_scope = "Specified" + environments = [octopusdeploy_environment.env_1.id, octopusdeploy_environment.env_2.id] + default_guided_failure_mode = "EnvironmentDefault" + force_package_download = true +} + +resource "octopusdeploy_runbook_process" "tenanted_runbook_process" { + runbook_id = octopusdeploy_runbook.tenanted_runbook.id + step { + condition = "Success" + name = "Hello world (using PowerShell)" + package_requirement = "LetOctopusDecide" + start_trigger = "StartAfterPrevious" + run_script_action { + can_be_used_for_project_versioning = false + condition = "Success" + is_disabled = false + is_required = true + name = "Hello world (using PowerShell)" + script_body = <<-EOT + Write-Host 'Hello world, using PowerShell' + #TODO: Experiment with steps of your own :) + Write-Host '[Learn more about the types of steps available in Octopus](https://g.octopushq.com/OnboardingAddStepsLearnMore)' + EOT + run_on_server = true + } + } +} + +resource "octopusdeploy_tag_set" "tagset_tag1" { + name = "tag1" + description = "Test tagset" + sort_order = 0 +} + +resource "octopusdeploy_tag" "tag_a" { + name = "a" + color = "#333333" + description = "tag a" + sort_order = 2 + tag_set_id = octopusdeploy_tag_set.tagset_tag1.id +} + +resource "octopusdeploy_tag" "tag_b" { + name = "b" + color = "#333333" + description = "tag b" + sort_order = 3 + tag_set_id = octopusdeploy_tag_set.tagset_tag1.id +} + +resource "octopusdeploy_tenant" "tenant_team_a" { + name = "Team A" + description = "Test tenant" + tenant_tags = ["tag1/a", "tag1/b"] + space_id = var.octopus_space_id + + depends_on = [octopusdeploy_tag.tag_a, octopusdeploy_tag.tag_b] + + project_environment { + environments = [octopusdeploy_environment.env_1.id, octopusdeploy_environment.env_2.id] + project_id = octopusdeploy_project.tenanted.id + } +} + +resource "octopusdeploy_tenant" "tenant_team_b" { + name = "Team B" + description = "Test tenant" + tenant_tags = ["tag1/a", "tag1/b"] + space_id = var.octopus_space_id + depends_on = [octopusdeploy_tag.tag_a, octopusdeploy_tag.tag_b] + + project_environment { + environments = [octopusdeploy_environment.env_1.id, octopusdeploy_environment.env_2.id] + project_id = octopusdeploy_project.tenanted.id + } +} + +resource "octopusdeploy_project_scheduled_trigger" "tenanted_trigger" { + name = "Cron Tenanted" + project_id = octopusdeploy_project.tenanted.id + space_id = octopusdeploy_project.tenanted.space_id + tenant_ids = [octopusdeploy_tenant.tenant_team_a.id, octopusdeploy_tenant.tenant_team_b.id] + deploy_new_release_action { + destination_environment_id = octopusdeploy_environment.env_1.id + } + cron_expression_schedule { + cron_expression = "0 0 06 * * Mon-Fri" + } +} + +resource "octopusdeploy_project_scheduled_trigger" "tenanted_runbook_trigger" { + name = "Cron Runbook" + description = "This is a Cron schedule" + project_id = octopusdeploy_project.tenanted.id + space_id = octopusdeploy_project.tenanted.space_id + tenant_ids = [octopusdeploy_tenant.tenant_team_a.id, octopusdeploy_tenant.tenant_team_b.id] + + run_runbook_action { + target_environment_ids = [octopusdeploy_environment.env_1.id] + runbook_id = octopusdeploy_runbook.tenanted_runbook.id + } + cron_expression_schedule { + cron_expression = "0 0 06 * * Mon-Fri" + } +} \ No newline at end of file diff --git a/terraform/53-scheduledprojecttrigger/config.tf b/terraform/53-scheduledprojecttrigger/config.tf new file mode 100644 index 000000000..8a9eaf8a9 --- /dev/null +++ b/terraform/53-scheduledprojecttrigger/config.tf @@ -0,0 +1,7 @@ +terraform { + required_providers { + octopusdeploy = { source = "OctopusDeployLabs/octopusdeploy", version = "0.11.3" } + // Use the option below when debugging + // octopusdeploy = { source = "octopus.com/com/octopusdeploy" } + } +} diff --git a/terraform/53-scheduledprojecttrigger/environments.tf b/terraform/53-scheduledprojecttrigger/environments.tf new file mode 100644 index 000000000..95719d3ca --- /dev/null +++ b/terraform/53-scheduledprojecttrigger/environments.tf @@ -0,0 +1,23 @@ +resource "octopusdeploy_environment" "env_1" { + name = "Env1" + space_id = var.octopus_space_id +} + +resource "octopusdeploy_environment" "env_2" { + name = "Env2" + space_id = var.octopus_space_id +} + +data "octopusdeploy_lifecycles" "lifecycle_default_lifecycle" { + ids = null + partial_name = "Default Lifecycle" + space_id = var.octopus_space_id + skip = 0 + take = 1 +} + +resource "octopusdeploy_project_group" "project_group_test" { + name = "Test" + space_id = var.octopus_space_id + description = "Test Description" +} diff --git a/terraform/53-scheduledprojecttrigger/provider.tf b/terraform/53-scheduledprojecttrigger/provider.tf new file mode 100644 index 000000000..a04197720 --- /dev/null +++ b/terraform/53-scheduledprojecttrigger/provider.tf @@ -0,0 +1,5 @@ +provider "octopusdeploy" { + address = "${var.octopus_server}" + api_key = "${var.octopus_apikey}" + space_id = "${var.octopus_space_id}" +} diff --git a/terraform/53-scheduledprojecttrigger/provider_vars.tf b/terraform/53-scheduledprojecttrigger/provider_vars.tf new file mode 100644 index 000000000..c7d93fe40 --- /dev/null +++ b/terraform/53-scheduledprojecttrigger/provider_vars.tf @@ -0,0 +1,18 @@ +variable "octopus_server" { + type = string + nullable = false + sensitive = false + description = "The URL of the Octopus server e.g. https://myinstance.octopus.app." +} +variable "octopus_apikey" { + type = string + nullable = false + sensitive = true + description = "The API key used to access the Octopus server. See https://octopus.com/docs/octopus-rest-api/how-to-create-an-api-key for details on creating an API key." +} +variable "octopus_space_id" { + type = string + nullable = false + sensitive = false + description = "The space ID to populate" +} diff --git a/terraform/53-scheduledprojecttrigger/space.tf b/terraform/53-scheduledprojecttrigger/space.tf new file mode 100644 index 000000000..ee59bdc80 --- /dev/null +++ b/terraform/53-scheduledprojecttrigger/space.tf @@ -0,0 +1,3 @@ +output "octopus_space_id" { + value = var.octopus_space_id +}