Skip to content

Commit

Permalink
feat: azure OIDC account support
Browse files Browse the repository at this point in the history
  • Loading branch information
benPearce1 authored Dec 5, 2023
1 parent fd21a66 commit ebc8a7f
Show file tree
Hide file tree
Showing 10 changed files with 362 additions and 5 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,6 @@ terraform/**/terrform.tfstate
terraform/**/.terraform/*

# Ignore the test results file
results.xml
results.xml

.DS_Store
12 changes: 12 additions & 0 deletions examples/resources/octopusdeploy_account/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ resource "octopusdeploy_account" "azure_service_principal_account" {
tenant_id = "00000000-0000-0000-0000-000000000000"
}

# create an Azure OpenID Connect account
resource "octopusdeploy_account" "azure_openid_connect_account" {
account_type = "AzureOIDC"
application_id = "00000000-0000-0000-0000-000000000000"
name = "Azure OpenID Connect Account (OK to Delete)"
subscription_id = "00000000-0000-0000-0000-000000000000"
tenant_id = "00000000-0000-0000-0000-000000000000"
executions_subject_keys = ["space", "project"]
health_subject_keys = ["space", "target"]
account_test_subject_keys = ["space", "type"]
}

# create an Azure subscription account
resource "octopusdeploy_account" "azure_subscription_account" {
account_type = "AzureSubscription"
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/OctopusDeploy/terraform-provider-octopusdeploy
go 1.20

require (
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.33.3
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.34.0
github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20230705105638-f5ef7c07973b
github.com/google/uuid v1.3.0
github.com/gruntwork-io/terratest v0.41.11
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugX
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
github.com/Microsoft/hcsshim v0.9.7 h1:mKNHW/Xvv1aFH87Jb6ERDzXTJTLPlmzfZ28VBFD/bfg=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.33.3 h1:5+ChMTskACIVLKj8YX45XAQfAjVnfm6ftL+n6Kc7dTY=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.33.3/go.mod h1:GZmFu6LmN8Yg0tEoZx3ytk9FnaH+84cWm7u5TdWZC6E=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.34.0 h1:S8h21VdVSHC8yfAk0Te8eHU/gkJ+3w2pv7Q/0kooK8I=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.34.0/go.mod h1:GZmFu6LmN8Yg0tEoZx3ytk9FnaH+84cWm7u5TdWZC6E=
github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20230705105638-f5ef7c07973b h1:XOBPcVHeDUYIpcag0yI8IYKiBL+5LLL8suysvlavQwI=
github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20230705105638-f5ef7c07973b/go.mod h1:E0hYVpZd61fXhzTozkxjiWEy+/yTRxAnr2SIE7k8ZSM=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
Expand Down
1 change: 1 addition & 0 deletions octopusdeploy/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func Provider() *schema.Provider {
"octopusdeploy_azure_cloud_service_deployment_target": resourceAzureCloudServiceDeploymentTarget(),
"octopusdeploy_azure_service_fabric_cluster_deployment_target": resourceAzureServiceFabricClusterDeploymentTarget(),
"octopusdeploy_azure_service_principal": resourceAzureServicePrincipalAccount(),
"octopusdeploy_azure_openid_connect": resourceAzureOpenIDConnectAccount(),
"octopusdeploy_azure_subscription_account": resourceAzureSubscriptionAccount(),
"octopusdeploy_azure_web_app_deployment_target": resourceAzureWebAppDeploymentTarget(),
"octopusdeploy_certificate": resourceCertificate(),
Expand Down
96 changes: 96 additions & 0 deletions octopusdeploy/resource_azure_oidc_account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package octopusdeploy

import (
"context"
"log"

"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/accounts"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
"github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceAzureOpenIDConnectAccount() *schema.Resource {
return &schema.Resource{
CreateContext: resourceAzureOpenIDConnectAccountCreate,
DeleteContext: resourceAzureOpenIDConnectAccountDelete,
Description: "This resource manages Azure OpenID Connect accounts in Octopus Deploy.",
Importer: getImporter(),
ReadContext: resourceAzureOpenIDConnectAccountRead,
Schema: getAzureOpenIdConnectAccountSchema(),
UpdateContext: resourceAzureOpenIDConnectAccountUpdate,
}
}

func resourceAzureOpenIDConnectAccountCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
account := expandAzureOpenIDConnectAccount(d)

log.Printf("[INFO] creating Azure OpenID Connect account: %#v", account)

client := m.(*client.Client)
createdAccount, err := accounts.Add(client, account)
if err != nil {
return diag.FromErr(err)
}

if err := setAzureOpenIDConnectAccount(ctx, d, createdAccount.(*accounts.AzureOIDCAccount)); err != nil {
return diag.FromErr(err)
}

d.SetId(createdAccount.GetID())

log.Printf("[INFO] Azure OpenID Connect account created (%s)", d.Id())
return nil
}

func resourceAzureOpenIDConnectAccountDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Printf("[INFO] deleting Azure OpenID Connect account (%s)", d.Id())

client := m.(*client.Client)
if err := accounts.DeleteByID(client, d.Get("space_id").(string), d.Id()); err != nil {
return diag.FromErr(err)
}

d.SetId("")

log.Printf("[INFO] Azure OpenID Connect account deleted")
return nil
}

func resourceAzureOpenIDConnectAccountRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Printf("[INFO] reading Azure OpenID Connect account (%s)", d.Id())

client := m.(*client.Client)
accountResource, err := accounts.GetByID(client, d.Get("space_id").(string), d.Id())
if err != nil {
return errors.ProcessApiError(ctx, d, err, "Azure OpenID Connect account")
}

azureOIDCAccount := accountResource.(*accounts.AzureOIDCAccount)
if err := setAzureOpenIDConnectAccount(ctx, d, azureOIDCAccount); err != nil {
return diag.FromErr(err)
}

log.Printf("[INFO] Azure OpenID Connect account read (%s)", d.Id())
return nil
}

func resourceAzureOpenIDConnectAccountUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
account := expandAzureOpenIDConnectAccount(d)

log.Printf("[INFO] updating Azure OpenID Connect account %#v", account)

client := m.(*client.Client)
updatedAccount, err := accounts.Update(client, account)
if err != nil {
return diag.FromErr(err)
}

if err := setAzureOpenIDConnectAccount(ctx, d, updatedAccount.(*accounts.AzureOIDCAccount)); err != nil {
return diag.FromErr(err)
}

log.Printf("[INFO] Azure OpenID Connect account updated (%s)", d.Id())
return nil
}
89 changes: 89 additions & 0 deletions octopusdeploy/resource_azure_oidc_account_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package octopusdeploy

import (
"fmt"
"testing"

"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core"
uuid "github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccOctopusDeployAzureOpenIDConnectAccountBasic(t *testing.T) {
localName := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha)
prefix := "octopusdeploy_azure_openid_connect." + localName

applicationID := uuid.New()
description := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha)
name := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha)
subscriptionID := uuid.New()
tenantedDeploymentMode := core.TenantedDeploymentModeTenantedOrUntenanted
tenantID := uuid.New()

execution_keys := []string{"space"}
health_keys := []string{"target"}
account_keys := []string{"type"}
audience := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha)

newDescription := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha)

resource.Test(t, resource.TestCase{
CheckDestroy: testAccountCheckDestroy,
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Check: resource.ComposeTestCheckFunc(
testAccountExists(prefix),
resource.TestCheckResourceAttr(prefix, "application_id", applicationID.String()),
resource.TestCheckResourceAttr(prefix, "description", description),
resource.TestCheckResourceAttr(prefix, "name", name),
resource.TestCheckResourceAttr(prefix, "subscription_id", subscriptionID.String()),
resource.TestCheckResourceAttr(prefix, "tenant_id", tenantID.String()),
resource.TestCheckResourceAttr(prefix, "tenanted_deployment_participation", string(tenantedDeploymentMode)),
resource.TestCheckResourceAttr(prefix, "execution_subject_keys", execution_keys[0]),
resource.TestCheckResourceAttr(prefix, "health_subject_keys", health_keys[0]),
resource.TestCheckResourceAttr(prefix, "account_test_subject_keys", account_keys[0]),
resource.TestCheckResourceAttr(prefix, "audience", audience),
),
Config: testAzureOpenIDConnectAccountBasic(localName, name, description, applicationID, tenantID, subscriptionID, tenantedDeploymentMode, execution_keys, health_keys, account_keys, audience),
},
{
Check: resource.ComposeTestCheckFunc(
testAccountExists(prefix),
resource.TestCheckResourceAttr(prefix, "application_id", applicationID.String()),
resource.TestCheckResourceAttr(prefix, "description", newDescription),
resource.TestCheckResourceAttr(prefix, "name", name),
resource.TestCheckResourceAttr(prefix, "subscription_id", subscriptionID.String()),
resource.TestCheckResourceAttr(prefix, "tenant_id", tenantID.String()),
resource.TestCheckResourceAttr(prefix, "tenanted_deployment_participation", string(tenantedDeploymentMode)),
resource.TestCheckResourceAttr(prefix, "execution_subject_keys", execution_keys[0]),
resource.TestCheckResourceAttr(prefix, "health_subject_keys", health_keys[0]),
resource.TestCheckResourceAttr(prefix, "account_test_subject_keys", account_keys[0]),
resource.TestCheckResourceAttr(prefix, "audience", audience),
),
Config: testAzureOpenIDConnectAccountBasic(localName, name, newDescription, applicationID, tenantID, subscriptionID, tenantedDeploymentMode, execution_keys, health_keys, account_keys, audience),
},
},
})
}

func testAzureOpenIDConnectAccountBasic(localName string, name string, description string, applicationID uuid.UUID, tenantID uuid.UUID, subscriptionID uuid.UUID, tenantedDeploymentParticipation core.TenantedDeploymentMode, execution_subject_keys []string, health_subject_keys []string, account_test_subject_keys []string, audience string) string {
return fmt.Sprintf(`resource "octopusdeploy_azure_openid_connect" "%s" {
application_id = "%s"
description = "%s"
name = "%s"
subscription_id = "%s"
tenant_id = "%s"
tenanted_deployment_participation = "%s"
execution_subject_keys = "%s"
health_subject_keys = "%s"
account_test_subject_keys = "%s"
audience = "%s"
}
data "octopusdeploy_accounts" "test" {
ids = [octopusdeploy_azure_openid_connect.%s.id]
}`, localName, applicationID, description, name, subscriptionID, tenantID, tenantedDeploymentParticipation, execution_subject_keys, health_subject_keys, account_test_subject_keys, audience, localName)
}
140 changes: 140 additions & 0 deletions octopusdeploy/schema_azure_oidc_account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package octopusdeploy

import (
"context"
"fmt"

"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/accounts"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core"
uuid "github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func expandAzureOpenIDConnectAccount(d *schema.ResourceData) *accounts.AzureOIDCAccount {
name := d.Get("name").(string)

applicationID, _ := uuid.Parse(d.Get("application_id").(string))
tenantID, _ := uuid.Parse(d.Get("tenant_id").(string))
subscriptionID, _ := uuid.Parse(d.Get("subscription_id").(string))

account, _ := accounts.NewAzureOIDCAccount(name, subscriptionID, tenantID, applicationID)
account.ID = d.Id()

if v, ok := d.GetOk("authentication_endpoint"); ok {
account.AuthenticationEndpoint = v.(string)
}

if v, ok := d.GetOk("description"); ok {
account.SetDescription(v.(string))
}

if v, ok := d.GetOk("environments"); ok {
account.EnvironmentIDs = getSliceFromTerraformTypeList(v)
}

if v, ok := d.GetOk("name"); ok {
account.SetName(v.(string))
}

if v, ok := d.GetOk("resource_manager_endpoint"); ok {
account.ResourceManagerEndpoint = v.(string)
}

if v, ok := d.GetOk("space_id"); ok {
account.SetSpaceID(v.(string))
}

if v, ok := d.GetOk("tenanted_deployment_participation"); ok {
account.TenantedDeploymentMode = core.TenantedDeploymentMode(v.(string))
}

if v, ok := d.GetOk("tenant_tags"); ok {
account.TenantTags = getSliceFromTerraformTypeList(v)
}

if v, ok := d.GetOk("tenants"); ok {
account.TenantIDs = getSliceFromTerraformTypeList(v)
}

if v, ok := d.GetOk("execution_subject_keys"); ok {
account.DeploymentSubjectKeys = getSliceFromTerraformTypeList(v)
}

if v, ok := d.GetOk("health_subject_keys"); ok {
account.HealthCheckSubjectKeys = getSliceFromTerraformTypeList(v)
}

if v, ok := d.GetOk("account_test_subject_keys"); ok {
account.AccountTestSubjectKeys = getSliceFromTerraformTypeList(v)
}

if v, ok := d.GetOk("audience"); ok {
account.Audience = v.(string)
}

return account
}

func getAzureOpenIdConnectAccountSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"application_id": getApplicationIDSchema(true),
"authentication_endpoint": getAuthenticationEndpointSchema(false),
"azure_environment": getAzureEnvironmentSchema(),
"description": getDescriptionSchema("Azure OpenID Connect account"),
"environments": getEnvironmentsSchema(),
"id": getIDSchema(),
"name": getNameSchema(true),
"resource_manager_endpoint": getResourceManagerEndpointSchema(false),
"space_id": getSpaceIDSchema(),
"subscription_id": getSubscriptionIDSchema(true),
"tenanted_deployment_participation": getTenantedDeploymentSchema(),
"tenants": getTenantsSchema(),
"tenant_id": getTenantIDSchema(true),
"tenant_tags": getTenantTagsSchema(),
"execution_subject_keys": getSubjectKeysSchema(),
"health_subject_keys": getSubjectKeysSchema(),
"account_test_subject_keys": getSubjectKeysSchema(),
"audience": getOidcAudienceSchema(),
}
}

func setAzureOpenIDConnectAccount(ctx context.Context, d *schema.ResourceData, account *accounts.AzureOIDCAccount) error {
d.Set("application_id", account.ApplicationID.String())
d.Set("authentication_endpoint", account.AuthenticationEndpoint)
d.Set("azure_environment", account.AzureEnvironment)
d.Set("description", account.GetDescription())
d.Set("id", account.GetID())
d.Set("name", account.GetName())
d.Set("resource_manager_endpoint", account.ResourceManagerEndpoint)
d.Set("space_id", account.GetSpaceID())
d.Set("subscription_id", account.SubscriptionID.String())
d.Set("tenanted_deployment_participation", account.GetTenantedDeploymentMode())
d.Set("tenant_id", account.TenantID.String())
d.Set("audience", account.Audience)

if err := d.Set("environments", account.GetEnvironmentIDs()); err != nil {
return fmt.Errorf("error setting environments: %s", err)
}

if err := d.Set("tenants", account.GetTenantIDs()); err != nil {
return fmt.Errorf("error setting tenants: %s", err)
}

if err := d.Set("tenant_tags", account.TenantTags); err != nil {
return fmt.Errorf("error setting tenant_tags: %s", err)
}

if err := d.Set("execution_subject_keys", account.DeploymentSubjectKeys); err != nil {
return fmt.Errorf("error setting execution_subject_keys: %s", err)
}

if err := d.Set("health_subject_keys", account.HealthCheckSubjectKeys); err != nil {
return fmt.Errorf("error setting health_subject_keys: %s", err)
}

if err := d.Set("account_test_subject_keys", account.AccountTestSubjectKeys); err != nil {
return fmt.Errorf("error setting account_test_subject_keys: %s", err)
}

return nil
}
1 change: 1 addition & 0 deletions octopusdeploy/schema_queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ func getQueryAccountType() *schema.Schema {
"AmazonWebServicesAccount",
"AmazonWebServicesRoleAccount",
"AzureServicePrincipal",
"AzureOIDC",
"AzureSubscription",
"None",
"SshKeyPair",
Expand Down
Loading

0 comments on commit ebc8a7f

Please sign in to comment.