feat: add use azapi option for subscription creation. (#253)

* Add initial config

* run make fmt and make docs

* run make lint

* run make test

* update test

* Fix Test

* Update GO Version

---------

Co-authored-by: Matt White <16320656+matt-FFFFFF@users.noreply.github.com>
This commit is contained in:
Luke Taylor 2023-09-27 23:19:56 +01:00 коммит произвёл GitHub
Родитель d17ad87e64
Коммит a3ed5b66d9
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 239 добавлений и 5 удалений

Просмотреть файл

@ -558,6 +558,27 @@ Type: `map(string)`
Default: `{}`
### <a name="input_subscription_use_azapi"></a> [subscription\_use\_azapi](#input\_subscription\_use\_azapi)
Description: Whether to create a new subscription using the azapi provider. This may be required if the principal running
terraform does not have the required permissions to create a subscription under the default management group.
If enabled, the following must also be supplied:
- `subscription_alias_name`
- `subscription_display_name`
- `subscription_billing_scope`
- `subscription_workload`
Optionally, supply the following to enable the placement of the subscription into a management group:
- `subscription_management_group_id`
- `subscription_management_group_association_enabled`
If disabled, supply the `subscription_id` variable to use an existing subscription instead.
> **Note**: When the subscription is destroyed, this module will try to remove the NetworkWatcherRG resource group using `az cli`.
> This requires the `az cli` tool be installed and authenticated.
> If the command fails for any reason, the provider will attempt to cancel the subscription anyway.
Type: `bool`
Default: `false`
### <a name="input_subscription_workload"></a> [subscription\_workload](#input\_subscription\_workload)
Description: The billing scope for the new subscription alias.

Просмотреть файл

@ -13,5 +13,6 @@ module "subscription" {
subscription_management_group_association_enabled = var.subscription_management_group_association_enabled
subscription_management_group_id = var.subscription_management_group_id
subscription_tags = var.subscription_tags
subscription_use_azapi = var.subscription_use_azapi
subscription_workload = var.subscription_workload
}

Просмотреть файл

@ -171,6 +171,14 @@ Type: `map(string)`
Default: `{}`
### <a name="input_subscription_use_azapi"></a> [subscription\_use\_azapi](#input\_subscription\_use\_azapi)
Description: Whether to use the azapi\_resource resource to create the subscription alias. This includes the subscription alias in the management group.
Type: `bool`
Default: `false`
### <a name="input_subscription_workload"></a> [subscription\_workload](#input\_subscription\_workload)
Description: The billing scope for the new subscription alias.
@ -188,6 +196,7 @@ Default: `""`
The following resources are used by this module:
- [azapi_resource.subscription](https://registry.terraform.io/providers/Azure/azapi/latest/docs/resources/resource) (resource)
- [azurerm_management_group_subscription_association.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/management_group_subscription_association) (resource)
- [azurerm_subscription.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subscription) (resource)

Просмотреть файл

@ -1,6 +1,6 @@
locals {
# subscription_id_alias is the id of the newly created subscription, if it exists.
subscription_id_alias = try(azurerm_subscription.this[0].subscription_id, null)
subscription_id_alias = try(azurerm_subscription.this[0].subscription_id, jsondecode(azapi_resource.subscription[0].output).properties.subscriptionId, null)
# subscription_id is the id of the newly created subscription, or the id supplied by var.subscription_id.
subscription_id = coalesce(local.subscription_id_alias, var.subscription_id)

Просмотреть файл

@ -1,6 +1,6 @@
# The azurerm_subscription resource represents the subscription alias that is being created.
resource "azurerm_subscription" "this" {
count = var.subscription_alias_enabled ? 1 : 0
count = var.subscription_alias_enabled && !var.subscription_use_azapi ? 1 : 0
subscription_name = var.subscription_display_name
alias = var.subscription_alias_name
billing_scope_id = var.subscription_billing_scope
@ -11,7 +11,28 @@ resource "azurerm_subscription" "this" {
# This resource ensures that we can manage the management group for the subscription
# throughout its lifecycle.
resource "azurerm_management_group_subscription_association" "this" {
count = var.subscription_management_group_association_enabled ? 1 : 0
count = var.subscription_management_group_association_enabled && !var.subscription_use_azapi ? 1 : 0
management_group_id = "/providers/Microsoft.Management/managementGroups/${var.subscription_management_group_id}"
subscription_id = "/subscriptions/${local.subscription_id}"
}
resource "azapi_resource" "subscription" {
count = var.subscription_alias_enabled && var.subscription_use_azapi ? 1 : 0
type = "Microsoft.Subscription/aliases@2021-10-01"
name = var.subscription_alias_name
parent_id = "/"
body = jsonencode({
properties = {
displayName = var.subscription_display_name
workload = var.subscription_workload
billingScope = var.subscription_billing_scope
additionalProperties = {
managementGroupId = var.subscription_management_group_association_enabled ? "/providers/Microsoft.Management/managementGroups/${var.subscription_management_group_id}" : null
tags = var.subscription_tags
}
}
})
response_export_values = ["properties.subscriptionId"]
}

Просмотреть файл

@ -15,7 +15,7 @@ DESCRIPTION
}
output "management_group_subscription_association_id" {
value = var.subscription_management_group_association_enabled ? azurerm_management_group_subscription_association.this[0].id : null
value = var.subscription_management_group_association_enabled ? try(azurerm_management_group_subscription_association.this[0].id, null) : null
description = <<DESCRIPTION
The management_group_subscription_association_id output is the ID of the management group subscription association.
Value will be null if `var.subscription_management_group_association_enabled` is false.

Просмотреть файл

@ -26,6 +26,10 @@ variable "subscription_alias_enabled" {
type = bool
}
variable "subscription_use_azapi" {
type = bool
}
resource "azapi_resource" "mg" {
type = "Microsoft.Management/managementGroups@2021-04-01"
parent_id = "/"
@ -41,6 +45,7 @@ module "subscription_test" {
subscription_billing_scope = var.subscription_billing_scope
subscription_management_group_association_enabled = var.subscription_management_group_association_enabled
subscription_alias_enabled = var.subscription_alias_enabled
subscription_use_azapi = var.subscription_use_azapi
}
output "subscription_id" {

Просмотреть файл

@ -155,3 +155,11 @@ subscription_tags = {
DESCRIPTION
default = {}
}
variable "subscription_use_azapi" {
type = bool
default = false
description = <<DESCRIPTION
Whether to use the azapi_resource resource to create the subscription alias. This includes the subscription alias in the management group.
DESCRIPTION
}

Просмотреть файл

@ -1,6 +1,6 @@
module github.com/Azure/terraform-azurerm-lz-vending/tests
go 1.20
go 1.21
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.2

Просмотреть файл

@ -116,6 +116,7 @@ cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQn
cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8=
cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08=
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo=
cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4=
cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w=
cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE=
@ -194,6 +195,7 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.1/go.mod h1:uE9zaUfEQT/nbQ
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2/go.mod h1:FbdwsQ2EzwvXxOPcMFYO8ogEc9uMMIj3YkmCdXdAFmk=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.1.0 h1:9pEy859umhY74QlSRXGb4ln4vjAcuqC5FuZfl2s2Plo=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.1.0/go.mod h1:xJQkWMSbOQ4W+B3OHpKfqyrOakAW1lOtJm9Mvd2ZHoY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 h1:QM6sE5k2ZT/vI5BEe0r7mqjsUSnhVBFbOsVkEuaEfiA=
@ -215,6 +217,7 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go v1.44.267 h1:Asrp6EMqqRxZvjK0NjzkWcrOk15RnWtupuUrUuZMabk=
github.com/aws/aws-sdk-go v1.44.267/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
@ -248,6 +251,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE=
github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
@ -281,7 +285,9 @@ github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M=
github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
@ -350,6 +356,7 @@ github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIG
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
@ -439,6 +446,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@ -468,7 +476,9 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk=
github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo=
github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E=
github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -477,10 +487,12 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
@ -822,6 +834,7 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

Просмотреть файл

@ -17,6 +17,7 @@ import (
)
var billingScope = os.Getenv("AZURE_BILLING_SCOPE")
var tenantId = os.Getenv("AZURE_TENANT_ID")
// TestDeploySubscriptionAliasValid tests the deployment of a subscription alias
// with valid input variables.
@ -55,6 +56,45 @@ func TestDeploySubscriptionAliasValid(t *testing.T) {
require.NoErrorf(t, err, "subscription id %s is not a valid uuid", sid)
}
// TestDeploySubscriptionAliasValid tests the deployment of a subscription alias
// with valid input variables.
// We also test RP registration here.
// This test uses the azapi provider.
func TestDeploySubscriptionAliasValidAzApi(t *testing.T) {
t.Parallel()
utils.PreCheckDeployTests(t)
v, err := getValidInputVariables(billingScope)
v["subscription_use_azapi"] = true
require.NoError(t, err)
test, err := setuptest.Dirs(moduleDir, "").WithVars(v).InitPlanShowWithPrepFunc(t, utils.AzureRmAndRequiredProviders)
require.NoError(t, err)
defer test.Cleanup()
check.InPlan(test.PlanStruct).NumberOfResourcesEquals(1).ErrorIsNil(t)
// Defer the cleanup of the subscription alias to the end of the test.
// Should be run after the Terraform destroy.
// We don't know the sub ID yet, so use zeros for now and then
// update it after the apply.
u := uuid.MustParse("00000000-0000-0000-0000-000000000000")
defer func() {
err := azureutils.CancelSubscription(t, &u)
t.Logf("cannot cancel subscription: %v", err)
}()
defer test.DestroyRetry(setuptest.DefaultRetry) //nolint:errcheck
test.ApplyIdempotent().ErrorIsNil(t)
sid, err := test.Output("subscription_id").GetValue()
assert.NoError(t, err)
sids, ok := sid.(string)
assert.True(t, ok, "subscription_id is not a string")
u, err = uuid.Parse(sids)
require.NoErrorf(t, err, "subscription id %s is not a valid uuid", sid)
}
// TestDeploySubscriptionAliasManagementGroupValid tests the deployment of a subscription alias
// with valid input variables.
func TestDeploySubscriptionAliasManagementGroupValid(t *testing.T) {
@ -98,6 +138,54 @@ func TestDeploySubscriptionAliasManagementGroupValid(t *testing.T) {
assert.NoErrorf(t, err, "subscription %s is not in management group %s", sid, v["subscription_management_group_id"].(string))
}
// TestDeploySubscriptionAliasManagementGroupValid tests the deployment of a subscription alias
// with valid input variables.
func TestDeploySubscriptionAliasManagementGroupValidAzApi(t *testing.T) {
t.Parallel()
utils.PreCheckDeployTests(t)
v, err := getValidInputVariables(billingScope)
require.NoError(t, err)
v["subscription_billing_scope"] = billingScope
v["subscription_management_group_id"] = v["subscription_alias_name"]
v["subscription_management_group_association_enabled"] = true
v["subscription_use_azapi"] = true
testDir := filepath.Join("testdata", "TestDeploySubscriptionAliasManagementGroupValid")
test, err := setuptest.Dirs(moduleDir, testDir).WithVars(v).InitPlanShowWithPrepFunc(t, utils.AzureRmAndRequiredProviders)
require.NoError(t, err)
defer test.Cleanup()
require.NoError(t, err)
// Defer the cleanup of the subscription alias to the end of the test.
// Should be run after the Terraform destroy.
// We don't know the sub ID yet, so use zeros for now and then
// update it after the apply.
u := uuid.MustParse("00000000-0000-0000-0000-000000000000")
defer func() {
err := azureutils.CancelSubscription(t, &u)
t.Logf("cannot cancel subscription: %v", err)
}()
// defer terraform destroy, but wrap in a try.Do to retry a few times
// due to eventual consistency of the subscription aliases API
defer test.DestroyRetry(setuptest.DefaultRetry) //nolint:errcheck
test.ApplyIdempotent().ErrorIsNil(t)
sid, err := terraform.OutputE(t, test.Options, "subscription_id")
assert.NoError(t, err)
u, err = uuid.Parse(sid)
assert.NoErrorf(t, err, "subscription id %s is not a valid uuid", sid)
err = azureutils.IsSubscriptionInManagementGroup(t, u, v["subscription_management_group_id"].(string))
assert.NoErrorf(t, err, "subscription %s is not in management group %s", sid, v["subscription_management_group_id"].(string))
if err := azureutils.SetSubscriptionManagementGroup(u, tenantId); err != nil {
t.Logf("cannot move subscription to tenant root group: %v", err)
}
}
// getValidInputVariables returns a set of valid input variables that can be used and modified for testing scenarios.
func getValidInputVariables(billingScope string) (map[string]any, error) {
r, err := utils.RandomHex(4)
@ -109,6 +197,7 @@ func getValidInputVariables(billingScope string) (map[string]any, error) {
"subscription_alias_name": name,
"subscription_display_name": name,
"subscription_billing_scope": billingScope,
"subscription_use_azapi": false,
"subscription_workload": "DevTest",
"subscription_alias_enabled": true,
}, nil

Просмотреть файл

@ -32,6 +32,26 @@ func TestSubscriptionAliasCreateValid(t *testing.T) {
check.InPlan(test.PlanStruct).That("azurerm_subscription.this[0]").Key("tags").HasValue(v["subscription_tags"]).ErrorIsNil(t)
}
// TestSubscriptionAliasCreateValid tests the validation functions with valid data,
// then creates a plan and compares the input variables to the planned values.
// This test uses the azapi provider.
func TestSubscriptionAliasCreateValidAzApi(t *testing.T) {
t.Parallel()
v := getMockInputVariables()
v["subscription_use_azapi"] = true
test, err := setuptest.Dirs(moduleDir, "").WithVars(v).InitPlanShowWithPrepFunc(t, utils.AzureRmAndRequiredProviders)
require.NoError(t, err)
defer test.Cleanup()
check.InPlan(test.PlanStruct).NumberOfResourcesEquals(1).ErrorIsNil(t)
check.InPlan(test.PlanStruct).That("azapi_resource.subscription[0]").Key("name").HasValue(v["subscription_alias_name"]).ErrorIsNil(t)
check.InPlan(test.PlanStruct).That("azapi_resource.subscription[0]").Key("body").Query("properties.billingScope").HasValue(v["subscription_billing_scope"]).ErrorIsNil(t)
check.InPlan(test.PlanStruct).That("azapi_resource.subscription[0]").Key("body").Query("properties.displayName").HasValue(v["subscription_display_name"]).ErrorIsNil(t)
check.InPlan(test.PlanStruct).That("azapi_resource.subscription[0]").Key("body").Query("properties.workload").HasValue(v["subscription_workload"]).ErrorIsNil(t)
check.InPlan(test.PlanStruct).That("azapi_resource.subscription[0]").Key("body").Query("properties.additionalProperties.tags").HasValue(v["subscription_tags"]).ErrorIsNil(t)
}
// TestSubscriptionAliasCreateValidWithManagementGroup tests the
// validation functions with valid data, including a destination management group,
// then creates a plan and compares the input variables to the planned values.
@ -55,6 +75,31 @@ func TestSubscriptionAliasCreateValidWithManagementGroup(t *testing.T) {
check.InPlan(test.PlanStruct).That("azurerm_management_group_subscription_association.this[0]").Key("management_group_id").HasValue(mgResId).ErrorIsNil(t)
}
// TestSubscriptionAliasCreateValidWithManagementGroupAzApi tests the
// validation functions with valid data, including a destination management group,
// then creates a plan and compares the input variables to the planned values.
// This test uses the azapi provider.
func TestSubscriptionAliasCreateValidWithManagementGroupAzApi(t *testing.T) {
t.Parallel()
v := getMockInputVariables()
v["subscription_management_group_id"] = "testdeploy"
v["subscription_management_group_association_enabled"] = true
v["subscription_use_azapi"] = true
test, err := setuptest.Dirs(moduleDir, "").WithVars(v).InitPlanShowWithPrepFunc(t, utils.AzureRmAndRequiredProviders)
require.NoError(t, err)
defer test.Cleanup()
check.InPlan(test.PlanStruct).NumberOfResourcesEquals(1).ErrorIsNil(t)
check.InPlan(test.PlanStruct).That("azapi_resource.subscription[0]").Key("name").HasValue(v["subscription_alias_name"]).ErrorIsNil(t)
check.InPlan(test.PlanStruct).That("azapi_resource.subscription[0]").Key("body").Query("properties.billingScope").HasValue(v["subscription_billing_scope"]).ErrorIsNil(t)
check.InPlan(test.PlanStruct).That("azapi_resource.subscription[0]").Key("body").Query("properties.workload").HasValue(v["subscription_workload"]).ErrorIsNil(t)
check.InPlan(test.PlanStruct).That("azapi_resource.subscription[0]").Key("body").Query("properties.additionalProperties.tags").HasValue(v["subscription_tags"]).ErrorIsNil(t)
mgResId := "/providers/Microsoft.Management/managementGroups/" + v["subscription_management_group_id"].(string)
check.InPlan(test.PlanStruct).That("azapi_resource.subscription[0]").Key("body").Query("properties.additionalProperties.managementGroupId").HasValue(mgResId).ErrorIsNil(t)
}
// TestSubscriptionExistingWithManagementGroup tests the
// validation functions with an existing subscription id, including a destination management group,
// then creates a plan and compares the input variables to the planned values.
@ -133,6 +178,7 @@ func getMockInputVariables() map[string]any {
"subscription_alias_name": "test-subscription-alias",
"subscription_billing_scope": "/providers/Microsoft.Billing/billingAccounts/0000000/enrollmentAccounts/000000",
"subscription_display_name": "test-subscription-alias",
"subscription_use_azapi": false,
"subscription_workload": "Production",
"subscription_tags": map[string]any{
"test-tag": "test-value",

Просмотреть файл

@ -148,3 +148,24 @@ subscription_tags = {
DESCRIPTION
default = {}
}
variable "subscription_use_azapi" {
type = bool
default = false
description = <<DESCRIPTION
Whether to create a new subscription using the azapi provider. This may be required if the principal running
terraform does not have the required permissions to create a subscription under the default management group.
If enabled, the following must also be supplied:
- `subscription_alias_name`
- `subscription_display_name`
- `subscription_billing_scope`
- `subscription_workload`
Optionally, supply the following to enable the placement of the subscription into a management group:
- `subscription_management_group_id`
- `subscription_management_group_association_enabled`
If disabled, supply the `subscription_id` variable to use an existing subscription instead.
> **Note**: When the subscription is destroyed, this module will try to remove the NetworkWatcherRG resource group using `az cli`.
> This requires the `az cli` tool be installed and authenticated.
> If the command fails for any reason, the provider will attempt to cancel the subscription anyway.
DESCRIPTION
}