Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azure OIDC account support #577

Merged
merged 3 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't know how we lived this long without it.

.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
Loading