From fb8acecd516cbbcc59f7a84041d55359a5816114 Mon Sep 17 00:00:00 2001 From: Simon Gottschlag Date: Mon, 21 Dec 2020 22:52:31 +0100 Subject: [PATCH 01/16] Initial commit of go-tf-preparer --- docker/go-tf-prepare/go.mod | 15 +++ docker/go-tf-prepare/go.sum | 71 +++++++++++++ docker/go-tf-prepare/main.go | 41 +++++++ docker/go-tf-prepare/pkg/azure/main.go | 142 +++++++++++++++++++++++++ 4 files changed, 269 insertions(+) create mode 100644 docker/go-tf-prepare/go.mod create mode 100644 docker/go-tf-prepare/go.sum create mode 100644 docker/go-tf-prepare/main.go create mode 100644 docker/go-tf-prepare/pkg/azure/main.go diff --git a/docker/go-tf-prepare/go.mod b/docker/go-tf-prepare/go.mod new file mode 100644 index 0000000..fc9a48a --- /dev/null +++ b/docker/go-tf-prepare/go.mod @@ -0,0 +1,15 @@ +module github.com/xenitab/github-actions/docker/go-tf-prepare + +go 1.15 + +require ( + github.com/Azure/azure-sdk-for-go/sdk/arm/resources/2020-06-01/armresources v0.1.0 + github.com/Azure/azure-sdk-for-go/sdk/arm/storage/2019-06-01/armstorage v0.1.0 + github.com/Azure/azure-sdk-for-go/sdk/armcore v0.5.1 + github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.4 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.7.0 + github.com/Azure/azure-sdk-for-go/sdk/to v0.1.2 + github.com/Azure/go-autorest/autorest v0.11.4 + github.com/jongio/azidext/go/azidext v0.1.0 + github.com/spf13/pflag v1.0.5 +) diff --git a/docker/go-tf-prepare/go.sum b/docker/go-tf-prepare/go.sum new file mode 100644 index 0000000..c1353c3 --- /dev/null +++ b/docker/go-tf-prepare/go.sum @@ -0,0 +1,71 @@ +github.com/Azure/azure-sdk-for-go v0.2.0-beta h1:wYBqYNMWr0WL2lcEZi+dlK9n+N0wJ0Pjs4BKeOnDjfQ= +github.com/Azure/azure-sdk-for-go v46.0.0+incompatible h1:4qlEOCDcDQZTGczYGzbGYCdJfVpZLIs8AEo5+MoXBPw= +github.com/Azure/azure-sdk-for-go v46.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v49.1.0+incompatible h1:Sz6TnzkzwsiEgD84Vujpjo0vEox9/UMeyLVWBCkZwQ4= +github.com/Azure/azure-sdk-for-go/sdk/arm/resources/2020-06-01/armresources v0.1.0 h1:lukRorBGhLmNospZsyC4ZBmKFSPjFm0EP2zhnURENSQ= +github.com/Azure/azure-sdk-for-go/sdk/arm/resources/2020-06-01/armresources v0.1.0/go.mod h1:BLfFIsqo/B2o7zUM/QbeTe9uu4FMpBjARi20XbmrOq8= +github.com/Azure/azure-sdk-for-go/sdk/arm/storage/2019-06-01/armstorage v0.1.0 h1:zMV0Tb4H7GfW0jNwH6ZO2hZt7O/gDMMih4248s8Wdg4= +github.com/Azure/azure-sdk-for-go/sdk/arm/storage/2019-06-01/armstorage v0.1.0/go.mod h1:tUy2yqX9i49NGIoOk7wSY9ULRlFsouD1oInDK+lDC1A= +github.com/Azure/azure-sdk-for-go/sdk/armcore v0.5.1 h1:VGqJCzGuqWjTvMspLSaWHiP1vUOA0lAdjoPXjhltfQQ= +github.com/Azure/azure-sdk-for-go/sdk/armcore v0.5.1/go.mod h1:+sBBoB6bha/qi//hAzfa4dd8EWRKBNYzSJjb9IccpXM= +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.10.0/go.mod h1:R+GJZ0mj7yxXtTENNLTzwkwro5zWzrEiZOdpIiN7Ypc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.1/go.mod h1:pElNP+u99BvCZD+0jOlhI9OC/NB2IDTOTGZOZH0Qhq8= +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.4 h1:7MfvHEWKfjZSKQNWERlXpHwCRoceEuQef/fB8CWmnQA= +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.4/go.mod h1:pElNP+u99BvCZD+0jOlhI9OC/NB2IDTOTGZOZH0Qhq8= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.2.0/go.mod h1:/XqWZ+BVfDwHnN6x+Ns+VH2Le0x0Yhks6I2DHkIyGGo= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.6.0/go.mod h1:BfjVb0eeNKsOveaOBnAgUv6nSq5hwScOz7mCm9lqUx8= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.7.0 h1:qD6HKtTRI0AMrXB+G0TJXxbB7c1gnG8zOROK8eKsbI0= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.7.0/go.mod h1:+5MTVs48VhHs2R1WpIFv7wy7vJ/acthpwlvcd2eN/Ok= +github.com/Azure/azure-sdk-for-go/sdk/internal v0.3.0/go.mod h1:Q+TCQnSr+clUU0JU+xrHZ3slYCxw17AOFdvWFpQXjAY= +github.com/Azure/azure-sdk-for-go/sdk/internal v0.5.0 h1:HG1ggl8L3ZkV/Ydanf7lKr5kkhhPGCpWdnr1J6v7cO4= +github.com/Azure/azure-sdk-for-go/sdk/internal v0.5.0/go.mod h1:k4KbFSunV/+0hOHL1vyFaPsiYQ1Vmvy1TBpmtvCDLZM= +github.com/Azure/azure-sdk-for-go/sdk/to v0.1.1/go.mod h1:UL/d4lvWAzSJUuX+19uKdN0ktyjoOyQhgY+HWNgtIYI= +github.com/Azure/azure-sdk-for-go/sdk/to v0.1.2 h1:TZTVOb/ce7nCmOZYga9+ELtPPVVFG2Px4s/w5OycYS0= +github.com/Azure/azure-sdk-for-go/sdk/to v0.1.2/go.mod h1:UL/d4lvWAzSJUuX+19uKdN0ktyjoOyQhgY+HWNgtIYI= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.4 h1:iWJqGEvip7mjibEqC/srXNdo+4wLEPiwlP/7dZLtoPc= +github.com/Azure/go-autorest/autorest v0.11.4/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest/adal v0.9.0 h1:SigMbuFNuKgc1xcGhaeapbh+8fgsu+GxgDRFyg7f5lM= +github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/autorest/validation v0.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= +github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/jongio/azidext v0.0.0-20201118191324-0df38e7e4890 h1:V5iynIWhcPF6Zd3pKjAEN/sbdNIq8DDxWbLHRcG3JwI= +github.com/jongio/azidext/go/azidext v0.1.0 h1:FlT+pmODYf82hqyQtE5C/Fajdt64wos88k2d7yhnhHk= +github.com/jongio/azidext/go/azidext v0.1.0/go.mod h1:v7DP8YodvY0fd6An/6j1A6OlU8SxPH1L7pjWcE/svik= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/docker/go-tf-prepare/main.go b/docker/go-tf-prepare/main.go new file mode 100644 index 0000000..21ec734 --- /dev/null +++ b/docker/go-tf-prepare/main.go @@ -0,0 +1,41 @@ +package main + +import ( + "context" + "fmt" + "os" + + flag "github.com/spf13/pflag" + "github.com/xenitab/github-actions/docker/go-tf-prepare/pkg/azure" +) + +func main() { + subscriptionID := flag.String("subscription-id", "", "Azure Subscription ID") + resourceGroupName := flag.String("resource-group-name", "", "Azure Resource Group Name") + resourceGroupLocation := flag.String("resource-group-location", "", "Azure Resource Group Location") + storageAccountName := flag.String("storage-account-name", "", "Azure Storage Account Name") + storageAccountContainer := flag.String("storage-account-container", "", "Azure Storage Container") + flag.Parse() + + ctx := context.Background() + err := azure.CreateResourceGroup(ctx, *resourceGroupName, *resourceGroupLocation, *subscriptionID) + if err != nil { + fmt.Printf("ERROR: %v \n", err) + os.Exit(1) + } + + err = azure.CreateStorageAccount(ctx, *resourceGroupName, *resourceGroupLocation, *storageAccountName, *subscriptionID) + if err != nil { + fmt.Printf("ERROR: %v \n", err) + os.Exit(1) + } + + err = azure.CreateStorageAccountContainer(ctx, *resourceGroupName, *storageAccountName, *storageAccountContainer, *subscriptionID) + if err != nil { + fmt.Printf("ERROR: %v \n", err) + os.Exit(1) + } + + os.Exit(0) + +} diff --git a/docker/go-tf-prepare/pkg/azure/main.go b/docker/go-tf-prepare/pkg/azure/main.go new file mode 100644 index 0000000..3f290e8 --- /dev/null +++ b/docker/go-tf-prepare/pkg/azure/main.go @@ -0,0 +1,142 @@ +package azure + +import ( + "context" + "fmt" + "log" + "strings" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/arm/resources/2020-06-01/armresources" + "github.com/Azure/azure-sdk-for-go/sdk/arm/storage/2019-06-01/armstorage" + "github.com/Azure/azure-sdk-for-go/sdk/armcore" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/to" +) + +// CreateResourceGroup creates Azure Resource Group (if it doesn't exist) or returns error +func CreateResourceGroup(ctx context.Context, resourceGroupName, resourceGroupLocation, subscriptionID string) error { + cred, err := azidentity.NewDefaultAzureCredential(nil) + if err != nil { + return fmt.Errorf("Failed Azure/CreateResourceGroup/azidentity.NewDefaultAzureCredential: %v", err) + } + + client := armresources.NewResourceGroupsClient(armcore.NewDefaultConnection(cred, nil), subscriptionID) + resourceGroupExists, err := client.CheckExistence(ctx, resourceGroupName, &armresources.ResourceGroupsCheckExistenceOptions{}) + if err != nil { + return fmt.Errorf("Failed Azure/CreateResourceGroup/client.CheckExistence: %v", err) + } + if !resourceGroupExists.Success { + _, err = client.CreateOrUpdate(ctx, resourceGroupName, armresources.ResourceGroup{ + Location: to.StringPtr(resourceGroupLocation), + }, nil) + if err != nil { + return fmt.Errorf("Failed Azure/CreateResourceGroup/client.CreateOrUpdate: %v", err) + } + fmt.Printf("INFO: Azure Resource Group (%s) created.\n", resourceGroupName) + return nil + } + + fmt.Printf("INFO: Azure Resource Group (%s) already exists.\n", resourceGroupName) + return nil +} + +// CreateStorageAccount creates Azure Storage Account (if it doesn't exist) or returns error +func CreateStorageAccount(ctx context.Context, resourceGroupName, resourceGroupLocation, storageAccountName, subscriptionID string) error { + cred, err := azidentity.NewDefaultAzureCredential(nil) + if err != nil { + return fmt.Errorf("Failed Azure/CreateStorageAccount/azidentity.NewDefaultAzureCredential: %v", err) + } + client := armstorage.NewStorageAccountsClient(armcore.NewDefaultConnection(cred, nil), subscriptionID) + _, err = client.GetProperties(ctx, resourceGroupName, storageAccountName, nil) + + if err == nil { + fmt.Printf("INFO: Azure Storage Account (%s) already exists.\n", storageAccountName) + return nil + } + + if err != nil && strings.Contains(err.Error(), "ResourceNotFound") { + res, err := client.CheckNameAvailability( + ctx, + armstorage.StorageAccountCheckNameAvailabilityParameters{ + Name: to.StringPtr(storageAccountName), + Type: to.StringPtr("Microsoft.Storage/storageAccounts"), + }, + nil) + + if err != nil { + return fmt.Errorf("Failed Azure/CreateStorageAccount/client.CheckNameAvailability: %v", err) + } + + if !*res.CheckNameAvailabilityResult.NameAvailable { + return fmt.Errorf("Failed Azure/CreateStorageAccount/client.CheckNameAvailability: Storage Account Name (%s) not available", storageAccountName) + } + + poller, err := client.BeginCreate( + ctx, + resourceGroupName, + storageAccountName, + armstorage.StorageAccountCreateParameters{ + SKU: &armstorage.SKU{ + Name: armstorage.SKUNameStandardGrs.ToPtr(), + Tier: armstorage.SKUTierStandard.ToPtr(), + }, + Kind: armstorage.KindBlobStorage.ToPtr(), + Location: to.StringPtr(resourceGroupLocation), + Properties: &armstorage.StorageAccountPropertiesCreateParameters{ + AccessTier: armstorage.AccessTierCool.ToPtr(), + }, + }, nil) + + if err != nil { + log.Fatalf("failed to obtain a response: %v", err) + } + + _, err = poller.PollUntilDone(context.Background(), 30*time.Second) + if err != nil { + return fmt.Errorf("Failed Azure/CreateStorageAccount/poller.PollUntilDone: %v", err) + } + + fmt.Printf("INFO: Azure Storage Account (%s) created.\n", storageAccountName) + return nil + } + + return fmt.Errorf("Failed Azure/CreateStorageAccount/client.GetProperties: %v", err) +} + +// CreateStorageAccountContainer creates Storage Account Container (if it doesn't exist) or returns error +func CreateStorageAccountContainer(ctx context.Context, resourceGroupName, storageAccountName, storageAccountContainer, subscriptionID string) error { + cred, err := azidentity.NewDefaultAzureCredential(nil) + if err != nil { + return fmt.Errorf("Failed Azure/CreateStorageAccountContainer/azidentity.NewDefaultAzureCredential: %v", err) + } + client := armstorage.NewBlobContainersClient(armcore.NewDefaultConnection(cred, nil), subscriptionID) + _, err = client.Get( + context.Background(), + resourceGroupName, + storageAccountName, + storageAccountContainer, nil) + + if err == nil { + fmt.Printf("INFO: Azure Storage Account Container (%s) already exists.\n", storageAccountContainer) + return nil + } + + if err != nil && strings.Contains(err.Error(), "The specified container does not exist") { + _, err := client.Create( + ctx, + resourceGroupName, + storageAccountName, + storageAccountContainer, + armstorage.BlobContainer{}, nil) + + if err != nil { + return fmt.Errorf("Failed Azure/CreateStorageAccountContainer/client.Create: %v", err) + } + + fmt.Printf("INFO: Azure Storage Account Container (%s) created.\n", storageAccountContainer) + return nil + } + + return fmt.Errorf("Failed Azure/CreateStorageAccountContainer/armstorage.NewBlobContainersClient: %v", err) +} From 5b9e668c015fbbf0d127e1b69744583730a3e433 Mon Sep 17 00:00:00 2001 From: Simon Gottschlag Date: Mon, 21 Dec 2020 23:14:59 +0100 Subject: [PATCH 02/16] Add Azure KeyVault --- docker/go-tf-prepare/go.mod | 1 + docker/go-tf-prepare/go.sum | 2 + docker/go-tf-prepare/main.go | 8 ++++ docker/go-tf-prepare/pkg/azure/main.go | 59 +++++++++++++++++++++++++- 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/docker/go-tf-prepare/go.mod b/docker/go-tf-prepare/go.mod index fc9a48a..cd54f66 100644 --- a/docker/go-tf-prepare/go.mod +++ b/docker/go-tf-prepare/go.mod @@ -3,6 +3,7 @@ module github.com/xenitab/github-actions/docker/go-tf-prepare go 1.15 require ( + github.com/Azure/azure-sdk-for-go/sdk/arm/keyvault/2019-09-01/armkeyvault v0.1.0 github.com/Azure/azure-sdk-for-go/sdk/arm/resources/2020-06-01/armresources v0.1.0 github.com/Azure/azure-sdk-for-go/sdk/arm/storage/2019-06-01/armstorage v0.1.0 github.com/Azure/azure-sdk-for-go/sdk/armcore v0.5.1 diff --git a/docker/go-tf-prepare/go.sum b/docker/go-tf-prepare/go.sum index c1353c3..c20bd14 100644 --- a/docker/go-tf-prepare/go.sum +++ b/docker/go-tf-prepare/go.sum @@ -2,6 +2,8 @@ github.com/Azure/azure-sdk-for-go v0.2.0-beta h1:wYBqYNMWr0WL2lcEZi+dlK9n+N0wJ0P github.com/Azure/azure-sdk-for-go v46.0.0+incompatible h1:4qlEOCDcDQZTGczYGzbGYCdJfVpZLIs8AEo5+MoXBPw= github.com/Azure/azure-sdk-for-go v46.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v49.1.0+incompatible h1:Sz6TnzkzwsiEgD84Vujpjo0vEox9/UMeyLVWBCkZwQ4= +github.com/Azure/azure-sdk-for-go/sdk/arm/keyvault/2019-09-01/armkeyvault v0.1.0 h1:4QOAacPzIU1dw03tLFRA64r6+EuzXnKUUR2eeMwes8Y= +github.com/Azure/azure-sdk-for-go/sdk/arm/keyvault/2019-09-01/armkeyvault v0.1.0/go.mod h1:3p+zC+iBXbnp1014TnjW42FHi594AKtpzIeJqbCGPks= github.com/Azure/azure-sdk-for-go/sdk/arm/resources/2020-06-01/armresources v0.1.0 h1:lukRorBGhLmNospZsyC4ZBmKFSPjFm0EP2zhnURENSQ= github.com/Azure/azure-sdk-for-go/sdk/arm/resources/2020-06-01/armresources v0.1.0/go.mod h1:BLfFIsqo/B2o7zUM/QbeTe9uu4FMpBjARi20XbmrOq8= github.com/Azure/azure-sdk-for-go/sdk/arm/storage/2019-06-01/armstorage v0.1.0 h1:zMV0Tb4H7GfW0jNwH6ZO2hZt7O/gDMMih4248s8Wdg4= diff --git a/docker/go-tf-prepare/main.go b/docker/go-tf-prepare/main.go index 21ec734..0354352 100644 --- a/docker/go-tf-prepare/main.go +++ b/docker/go-tf-prepare/main.go @@ -11,10 +11,12 @@ import ( func main() { subscriptionID := flag.String("subscription-id", "", "Azure Subscription ID") + tenantID := flag.String("tenant-id", "", "Azure Tenant ID") resourceGroupName := flag.String("resource-group-name", "", "Azure Resource Group Name") resourceGroupLocation := flag.String("resource-group-location", "", "Azure Resource Group Location") storageAccountName := flag.String("storage-account-name", "", "Azure Storage Account Name") storageAccountContainer := flag.String("storage-account-container", "", "Azure Storage Container") + keyVaultName := flag.String("keyvault-name", "", "Azure KeyVault Name") flag.Parse() ctx := context.Background() @@ -36,6 +38,12 @@ func main() { os.Exit(1) } + err = azure.CreateKeyVault(ctx, *resourceGroupName, *resourceGroupLocation, *keyVaultName, *subscriptionID, *tenantID) + if err != nil { + fmt.Printf("ERROR: %v \n", err) + os.Exit(1) + } + os.Exit(0) } diff --git a/docker/go-tf-prepare/pkg/azure/main.go b/docker/go-tf-prepare/pkg/azure/main.go index 3f290e8..7342301 100644 --- a/docker/go-tf-prepare/pkg/azure/main.go +++ b/docker/go-tf-prepare/pkg/azure/main.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/Azure/azure-sdk-for-go/sdk/arm/keyvault/2019-09-01/armkeyvault" "github.com/Azure/azure-sdk-for-go/sdk/arm/resources/2020-06-01/armresources" "github.com/Azure/azure-sdk-for-go/sdk/arm/storage/2019-06-01/armstorage" "github.com/Azure/azure-sdk-for-go/sdk/armcore" @@ -92,7 +93,7 @@ func CreateStorageAccount(ctx context.Context, resourceGroupName, resourceGroupL log.Fatalf("failed to obtain a response: %v", err) } - _, err = poller.PollUntilDone(context.Background(), 30*time.Second) + _, err = poller.PollUntilDone(ctx, 30*time.Second) if err != nil { return fmt.Errorf("Failed Azure/CreateStorageAccount/poller.PollUntilDone: %v", err) } @@ -112,7 +113,7 @@ func CreateStorageAccountContainer(ctx context.Context, resourceGroupName, stora } client := armstorage.NewBlobContainersClient(armcore.NewDefaultConnection(cred, nil), subscriptionID) _, err = client.Get( - context.Background(), + ctx, resourceGroupName, storageAccountName, storageAccountContainer, nil) @@ -140,3 +141,57 @@ func CreateStorageAccountContainer(ctx context.Context, resourceGroupName, stora return fmt.Errorf("Failed Azure/CreateStorageAccountContainer/armstorage.NewBlobContainersClient: %v", err) } + +// CreateKeyVault creates Azure Key Vault (if it doesn't exist) or returns error +func CreateKeyVault(ctx context.Context, resourceGroupName, resourceGroupLocation, keyVaultName, subscriptionID, tenantID string) error { + cred, err := azidentity.NewDefaultAzureCredential(nil) + if err != nil { + return fmt.Errorf("Failed Azure/CreateKeyVault/azidentity.NewDefaultAzureCredential: %v", err) + } + client := armkeyvault.NewVaultsClient(armcore.NewDefaultConnection(cred, nil), subscriptionID) + + _, err = client.Get(ctx, resourceGroupName, keyVaultName, nil) + if err == nil { + fmt.Printf("INFO: Azure KeyVault (%s) already exists.\n", keyVaultName) + return nil + } + + if err != nil && strings.Contains(err.Error(), "ResourceNotFound") { + keyVaultNameAvailable, err := client.CheckNameAvailability(ctx, armkeyvault.VaultCheckNameAvailabilityParameters{Name: to.StringPtr(keyVaultName), Type: to.StringPtr("Microsoft.KeyVault/vaults")}, nil) + if err != nil { + return fmt.Errorf("Failed Azure/CreateKeyVault/client.CheckNameAvailability: %v", err) + } + + if !*keyVaultNameAvailable.CheckNameAvailabilityResult.NameAvailable { + return fmt.Errorf("Failed Azure/CreateKeyVault/client.CheckNameAvailability: Azure KeyVault Name (%s) not available", keyVaultName) + } + + poll, err := client.BeginCreateOrUpdate( + ctx, + resourceGroupName, + keyVaultName, + armkeyvault.VaultCreateOrUpdateParameters{ + Location: to.StringPtr(resourceGroupLocation), + Properties: &armkeyvault.VaultProperties{ + TenantID: to.StringPtr(tenantID), + SKU: &armkeyvault.SKU{ + Family: armkeyvault.SKUFamilyA.ToPtr(), + Name: armkeyvault.SKUNameStandard.ToPtr(), + }, + AccessPolicies: &[]armkeyvault.AccessPolicyEntry{}, + }, + }, nil) + if err != nil { + return fmt.Errorf("Failed Azure/CreateKeyVault/client.BeginCreateOrUpdate: %v", err) + } + _, err = poll.PollUntilDone(ctx, 5*time.Second) + if err != nil { + return fmt.Errorf("Failed Azure/CreateKeyVault/poll.PollUntilDone: %v", err) + } + + fmt.Printf("INFO: Azure KeyVault (%s) created.\n", keyVaultName) + return nil + } + + return fmt.Errorf("Failed Azure/CreateKeyVault/client.Get: %v", err) +} From a5cfe324eb6e2e1a5891cc053642ae0f141e3a52 Mon Sep 17 00:00:00 2001 From: Simon Gottschlag Date: Tue, 22 Dec 2020 08:41:29 +0100 Subject: [PATCH 03/16] Add resource locks --- docker/go-tf-prepare/go.mod | 5 ++-- docker/go-tf-prepare/go.sum | 2 ++ docker/go-tf-prepare/main.go | 12 ++++++++++ docker/go-tf-prepare/pkg/azure/main.go | 32 ++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/docker/go-tf-prepare/go.mod b/docker/go-tf-prepare/go.mod index cd54f66..378d255 100644 --- a/docker/go-tf-prepare/go.mod +++ b/docker/go-tf-prepare/go.mod @@ -3,14 +3,15 @@ module github.com/xenitab/github-actions/docker/go-tf-prepare go 1.15 require ( + github.com/Azure/azure-sdk-for-go v46.0.0+incompatible github.com/Azure/azure-sdk-for-go/sdk/arm/keyvault/2019-09-01/armkeyvault v0.1.0 github.com/Azure/azure-sdk-for-go/sdk/arm/resources/2020-06-01/armresources v0.1.0 github.com/Azure/azure-sdk-for-go/sdk/arm/storage/2019-06-01/armstorage v0.1.0 github.com/Azure/azure-sdk-for-go/sdk/armcore v0.5.1 - github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.4 + github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.4 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.7.0 github.com/Azure/azure-sdk-for-go/sdk/to v0.1.2 - github.com/Azure/go-autorest/autorest v0.11.4 + github.com/Azure/go-autorest/autorest v0.11.4 // indirect github.com/jongio/azidext/go/azidext v0.1.0 github.com/spf13/pflag v1.0.5 ) diff --git a/docker/go-tf-prepare/go.sum b/docker/go-tf-prepare/go.sum index c20bd14..7839522 100644 --- a/docker/go-tf-prepare/go.sum +++ b/docker/go-tf-prepare/go.sum @@ -33,7 +33,9 @@ github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQW github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/autorest/validation v0.3.0 h1:3I9AAI63HfcLtphd9g39ruUwRI+Ca+z/f36KHPFRUss= github.com/Azure/go-autorest/autorest/validation v0.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= diff --git a/docker/go-tf-prepare/main.go b/docker/go-tf-prepare/main.go index 0354352..09a6f6e 100644 --- a/docker/go-tf-prepare/main.go +++ b/docker/go-tf-prepare/main.go @@ -32,6 +32,12 @@ func main() { os.Exit(1) } + err = azure.CreateResourceLock(ctx, *resourceGroupName, "Microsoft.Storage", "", "storageAccounts", *storageAccountName, "DoNotDelete", *subscriptionID) + if err != nil { + fmt.Printf("ERROR: %v \n", err) + os.Exit(1) + } + err = azure.CreateStorageAccountContainer(ctx, *resourceGroupName, *storageAccountName, *storageAccountContainer, *subscriptionID) if err != nil { fmt.Printf("ERROR: %v \n", err) @@ -44,6 +50,12 @@ func main() { os.Exit(1) } + err = azure.CreateResourceLock(ctx, *resourceGroupName, "Microsoft.KeyVault", "", "vaults", *keyVaultName, "DoNotDelete", *subscriptionID) + if err != nil { + fmt.Printf("ERROR: %v \n", err) + os.Exit(1) + } + os.Exit(0) } diff --git a/docker/go-tf-prepare/pkg/azure/main.go b/docker/go-tf-prepare/pkg/azure/main.go index 7342301..c13e769 100644 --- a/docker/go-tf-prepare/pkg/azure/main.go +++ b/docker/go-tf-prepare/pkg/azure/main.go @@ -13,6 +13,8 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/armcore" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/to" + "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2016-09-01/locks" + "github.com/jongio/azidext/go/azidext" ) // CreateResourceGroup creates Azure Resource Group (if it doesn't exist) or returns error @@ -195,3 +197,33 @@ func CreateKeyVault(ctx context.Context, resourceGroupName, resourceGroupLocatio return fmt.Errorf("Failed Azure/CreateKeyVault/client.Get: %v", err) } + +// CreateResourceLock creates Azure Resource Lock (if it doesn't exist) or return error +func CreateResourceLock(ctx context.Context, resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, lockName, subscriptionID string) error { + client := locks.NewManagementLocksClient(subscriptionID) + + authorizer, err := azidext.NewDefaultAzureCredentialAdapter(nil) + if err != nil { + return fmt.Errorf("Failed Azure/CreateResourceLock/azidext.NewDefaultAzureCredentialAdapter: %v", err) + } + + client.Authorizer = authorizer + + _, err = client.GetAtResourceLevel(ctx, resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, lockName) + if err == nil { + fmt.Printf("INFO: Azure Resource Lock (%s/%s/%s/%s) already exists.\n", resourceGroupName, resourceProviderNamespace, resourceType, resourceName) + return nil + } + + if err != nil && strings.Contains(err.Error(), "LockNotFound") { + _, err = client.CreateOrUpdateAtResourceLevel(ctx, resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, lockName, locks.ManagementLockObject{ManagementLockProperties: &locks.ManagementLockProperties{Level: "CanNotDelete", Notes: to.StringPtr("CanNotDelete")}}) + if err != nil { + return fmt.Errorf("Failed Azure/CreateResourceLock/client.CreateOrUpdateAtResourceLevel: %v", err) + } + + fmt.Printf("INFO: Azure Resource Lock (%s/%s/%s/%s) created.\n", resourceGroupName, resourceProviderNamespace, resourceType, resourceName) + return nil + } + + return fmt.Errorf("Failed Azure/CreateResourceLock/client.GetAtResourceLevel: %v", err) +} From 1511d87708537ecc4ddd85b16af92ab5c61d2f48 Mon Sep 17 00:00:00 2001 From: Simon Gottschlag Date: Tue, 22 Dec 2020 16:26:52 +0100 Subject: [PATCH 04/16] Update logging and add CreateKeyVaultAccessPolicy --- docker/go-tf-prepare/go.mod | 5 +- docker/go-tf-prepare/go.sum | 12 +- docker/go-tf-prepare/main.go | 22 ++- docker/go-tf-prepare/pkg/azure/main.go | 214 +++++++++++++++++++++---- 4 files changed, 209 insertions(+), 44 deletions(-) diff --git a/docker/go-tf-prepare/go.mod b/docker/go-tf-prepare/go.mod index 378d255..7149057 100644 --- a/docker/go-tf-prepare/go.mod +++ b/docker/go-tf-prepare/go.mod @@ -8,10 +8,11 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/arm/resources/2020-06-01/armresources v0.1.0 github.com/Azure/azure-sdk-for-go/sdk/arm/storage/2019-06-01/armstorage v0.1.0 github.com/Azure/azure-sdk-for-go/sdk/armcore v0.5.1 - github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.4 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.4 github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.7.0 github.com/Azure/azure-sdk-for-go/sdk/to v0.1.2 - github.com/Azure/go-autorest/autorest v0.11.4 // indirect + github.com/go-logr/logr v0.3.0 + github.com/go-logr/stdr v0.3.0 github.com/jongio/azidext/go/azidext v0.1.0 github.com/spf13/pflag v1.0.5 ) diff --git a/docker/go-tf-prepare/go.sum b/docker/go-tf-prepare/go.sum index 7839522..77662cc 100644 --- a/docker/go-tf-prepare/go.sum +++ b/docker/go-tf-prepare/go.sum @@ -1,7 +1,5 @@ -github.com/Azure/azure-sdk-for-go v0.2.0-beta h1:wYBqYNMWr0WL2lcEZi+dlK9n+N0wJ0Pjs4BKeOnDjfQ= github.com/Azure/azure-sdk-for-go v46.0.0+incompatible h1:4qlEOCDcDQZTGczYGzbGYCdJfVpZLIs8AEo5+MoXBPw= github.com/Azure/azure-sdk-for-go v46.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v49.1.0+incompatible h1:Sz6TnzkzwsiEgD84Vujpjo0vEox9/UMeyLVWBCkZwQ4= github.com/Azure/azure-sdk-for-go/sdk/arm/keyvault/2019-09-01/armkeyvault v0.1.0 h1:4QOAacPzIU1dw03tLFRA64r6+EuzXnKUUR2eeMwes8Y= github.com/Azure/azure-sdk-for-go/sdk/arm/keyvault/2019-09-01/armkeyvault v0.1.0/go.mod h1:3p+zC+iBXbnp1014TnjW42FHi594AKtpzIeJqbCGPks= github.com/Azure/azure-sdk-for-go/sdk/arm/resources/2020-06-01/armresources v0.1.0 h1:lukRorBGhLmNospZsyC4ZBmKFSPjFm0EP2zhnURENSQ= @@ -32,6 +30,7 @@ github.com/Azure/go-autorest/autorest/adal v0.9.0 h1:SigMbuFNuKgc1xcGhaeapbh+8fg github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.0 h1:z20OWOSG5aCye0HEkDp6TPmP17ZcfeMxPi6HnSALa8c= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= @@ -41,19 +40,26 @@ github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8 github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/go-logr/logr v0.3.0 h1:q4c+kbcR0d5rSurhBR8dIgieOaYpXtsdTYfx22Cu6rs= +github.com/go-logr/logr v0.3.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/stdr v0.3.0 h1:GzFt/sOHlPMqsh46UTAaIDWPFq3DdNGcF4rwMjhTBVo= +github.com/go-logr/stdr v0.3.0/go.mod h1:NO1vneyJDqKVgJYnxhwXWWmQPOvNM391IG3H8ql3jiA= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/jongio/azidext v0.0.0-20201118191324-0df38e7e4890 h1:V5iynIWhcPF6Zd3pKjAEN/sbdNIq8DDxWbLHRcG3JwI= github.com/jongio/azidext/go/azidext v0.1.0 h1:FlT+pmODYf82hqyQtE5C/Fajdt64wos88k2d7yhnhHk= github.com/jongio/azidext/go/azidext v0.1.0/go.mod h1:v7DP8YodvY0fd6An/6j1A6OlU8SxPH1L7pjWcE/svik= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/docker/go-tf-prepare/main.go b/docker/go-tf-prepare/main.go index 09a6f6e..899ecb2 100644 --- a/docker/go-tf-prepare/main.go +++ b/docker/go-tf-prepare/main.go @@ -2,9 +2,11 @@ package main import ( "context" - "fmt" + stdlog "log" "os" + "github.com/go-logr/logr" + "github.com/go-logr/stdr" flag "github.com/spf13/pflag" "github.com/xenitab/github-actions/docker/go-tf-prepare/pkg/azure" ) @@ -19,40 +21,44 @@ func main() { keyVaultName := flag.String("keyvault-name", "", "Azure KeyVault Name") flag.Parse() - ctx := context.Background() + stdr.SetVerbosity(1) + log := stdr.New(stdlog.New(os.Stderr, "", stdlog.LstdFlags|stdlog.Lshortfile)) + log = log.WithName("go-tf-preparer") + + ctx := logr.NewContext(context.Background(), log) + err := azure.CreateResourceGroup(ctx, *resourceGroupName, *resourceGroupLocation, *subscriptionID) if err != nil { - fmt.Printf("ERROR: %v \n", err) os.Exit(1) } err = azure.CreateStorageAccount(ctx, *resourceGroupName, *resourceGroupLocation, *storageAccountName, *subscriptionID) if err != nil { - fmt.Printf("ERROR: %v \n", err) os.Exit(1) } err = azure.CreateResourceLock(ctx, *resourceGroupName, "Microsoft.Storage", "", "storageAccounts", *storageAccountName, "DoNotDelete", *subscriptionID) if err != nil { - fmt.Printf("ERROR: %v \n", err) os.Exit(1) } err = azure.CreateStorageAccountContainer(ctx, *resourceGroupName, *storageAccountName, *storageAccountContainer, *subscriptionID) if err != nil { - fmt.Printf("ERROR: %v \n", err) os.Exit(1) } err = azure.CreateKeyVault(ctx, *resourceGroupName, *resourceGroupLocation, *keyVaultName, *subscriptionID, *tenantID) if err != nil { - fmt.Printf("ERROR: %v \n", err) os.Exit(1) } err = azure.CreateResourceLock(ctx, *resourceGroupName, "Microsoft.KeyVault", "", "vaults", *keyVaultName, "DoNotDelete", *subscriptionID) if err != nil { - fmt.Printf("ERROR: %v \n", err) + os.Exit(1) + } + + err = azure.CreateKeyVaultAccessPolicy(ctx, *resourceGroupName, *resourceGroupLocation, *keyVaultName, *subscriptionID, *tenantID) + if err != nil { os.Exit(1) } diff --git a/docker/go-tf-prepare/pkg/azure/main.go b/docker/go-tf-prepare/pkg/azure/main.go index c13e769..c776f30 100644 --- a/docker/go-tf-prepare/pkg/azure/main.go +++ b/docker/go-tf-prepare/pkg/azure/main.go @@ -3,7 +3,6 @@ package azure import ( "context" "fmt" - "log" "strings" "time" @@ -11,50 +10,62 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/arm/resources/2020-06-01/armresources" "github.com/Azure/azure-sdk-for-go/sdk/arm/storage/2019-06-01/armstorage" "github.com/Azure/azure-sdk-for-go/sdk/armcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/to" + "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2016-09-01/locks" + "github.com/go-logr/logr" "github.com/jongio/azidext/go/azidext" ) // CreateResourceGroup creates Azure Resource Group (if it doesn't exist) or returns error func CreateResourceGroup(ctx context.Context, resourceGroupName, resourceGroupLocation, subscriptionID string) error { + log := logr.FromContext(ctx) + cred, err := azidentity.NewDefaultAzureCredential(nil) if err != nil { - return fmt.Errorf("Failed Azure/CreateResourceGroup/azidentity.NewDefaultAzureCredential: %v", err) + log.Error(err, "azidentity.NewDefaultAzureCredential") + return err } client := armresources.NewResourceGroupsClient(armcore.NewDefaultConnection(cred, nil), subscriptionID) resourceGroupExists, err := client.CheckExistence(ctx, resourceGroupName, &armresources.ResourceGroupsCheckExistenceOptions{}) if err != nil { - return fmt.Errorf("Failed Azure/CreateResourceGroup/client.CheckExistence: %v", err) + log.Error(err, "client.CheckExistence") + return err } if !resourceGroupExists.Success { _, err = client.CreateOrUpdate(ctx, resourceGroupName, armresources.ResourceGroup{ Location: to.StringPtr(resourceGroupLocation), }, nil) if err != nil { - return fmt.Errorf("Failed Azure/CreateResourceGroup/client.CreateOrUpdate: %v", err) + log.Error(err, "client.CreateOrUpdate") + return err } - fmt.Printf("INFO: Azure Resource Group (%s) created.\n", resourceGroupName) + + log.Info("Azure Resource Group created", "resourceGroupName", resourceGroupName) return nil } - fmt.Printf("INFO: Azure Resource Group (%s) already exists.\n", resourceGroupName) + log.Info("Azure Resource Group already exists", "resourceGroupName", resourceGroupName) return nil } // CreateStorageAccount creates Azure Storage Account (if it doesn't exist) or returns error func CreateStorageAccount(ctx context.Context, resourceGroupName, resourceGroupLocation, storageAccountName, subscriptionID string) error { + log := logr.FromContext(ctx) + cred, err := azidentity.NewDefaultAzureCredential(nil) if err != nil { - return fmt.Errorf("Failed Azure/CreateStorageAccount/azidentity.NewDefaultAzureCredential: %v", err) + log.Error(err, "azidentity.NewDefaultAzureCredential") + return err } client := armstorage.NewStorageAccountsClient(armcore.NewDefaultConnection(cred, nil), subscriptionID) _, err = client.GetProperties(ctx, resourceGroupName, storageAccountName, nil) if err == nil { - fmt.Printf("INFO: Azure Storage Account (%s) already exists.\n", storageAccountName) + log.Info("Azure Storage Account already exists", "storageAccountName", storageAccountName) return nil } @@ -68,11 +79,13 @@ func CreateStorageAccount(ctx context.Context, resourceGroupName, resourceGroupL nil) if err != nil { - return fmt.Errorf("Failed Azure/CreateStorageAccount/client.CheckNameAvailability: %v", err) + log.Error(err, "client.CheckNameAvailability") + return err } if !*res.CheckNameAvailabilityResult.NameAvailable { - return fmt.Errorf("Failed Azure/CreateStorageAccount/client.CheckNameAvailability: Storage Account Name (%s) not available", storageAccountName) + log.Error(err, "client.CheckNameAvailability: Azure Storage Account Name not available", "storageAccountName", storageAccountName) + return err } poller, err := client.BeginCreate( @@ -92,26 +105,32 @@ func CreateStorageAccount(ctx context.Context, resourceGroupName, resourceGroupL }, nil) if err != nil { - log.Fatalf("failed to obtain a response: %v", err) + log.Error(err, "client.BeginCreate") + return err } _, err = poller.PollUntilDone(ctx, 30*time.Second) if err != nil { - return fmt.Errorf("Failed Azure/CreateStorageAccount/poller.PollUntilDone: %v", err) + log.Error(err, "poller.PollUntilDone") + return err } - fmt.Printf("INFO: Azure Storage Account (%s) created.\n", storageAccountName) + log.Info("Azure Storage Account created", "storageAccountName", storageAccountName) return nil } - return fmt.Errorf("Failed Azure/CreateStorageAccount/client.GetProperties: %v", err) + log.Error(err, "client.GetProperties") + return err } // CreateStorageAccountContainer creates Storage Account Container (if it doesn't exist) or returns error func CreateStorageAccountContainer(ctx context.Context, resourceGroupName, storageAccountName, storageAccountContainer, subscriptionID string) error { + log := logr.FromContext(ctx) + cred, err := azidentity.NewDefaultAzureCredential(nil) if err != nil { - return fmt.Errorf("Failed Azure/CreateStorageAccountContainer/azidentity.NewDefaultAzureCredential: %v", err) + log.Error(err, "azidentity.NewDefaultAzureCredential") + return err } client := armstorage.NewBlobContainersClient(armcore.NewDefaultConnection(cred, nil), subscriptionID) _, err = client.Get( @@ -121,7 +140,7 @@ func CreateStorageAccountContainer(ctx context.Context, resourceGroupName, stora storageAccountContainer, nil) if err == nil { - fmt.Printf("INFO: Azure Storage Account Container (%s) already exists.\n", storageAccountContainer) + log.Info("Azure Storage Account Container already exists", "storageAccountContainer", storageAccountContainer) return nil } @@ -134,38 +153,45 @@ func CreateStorageAccountContainer(ctx context.Context, resourceGroupName, stora armstorage.BlobContainer{}, nil) if err != nil { - return fmt.Errorf("Failed Azure/CreateStorageAccountContainer/client.Create: %v", err) + log.Error(err, "client.Create") + return err } - fmt.Printf("INFO: Azure Storage Account Container (%s) created.\n", storageAccountContainer) + log.Info("Azure Storage Account Container created", "storageAccountContainer", storageAccountContainer) return nil } - return fmt.Errorf("Failed Azure/CreateStorageAccountContainer/armstorage.NewBlobContainersClient: %v", err) + log.Error(err, "armstorage.NewBlobContainersClient") + return err } // CreateKeyVault creates Azure Key Vault (if it doesn't exist) or returns error func CreateKeyVault(ctx context.Context, resourceGroupName, resourceGroupLocation, keyVaultName, subscriptionID, tenantID string) error { + log := logr.FromContext(ctx) + cred, err := azidentity.NewDefaultAzureCredential(nil) if err != nil { - return fmt.Errorf("Failed Azure/CreateKeyVault/azidentity.NewDefaultAzureCredential: %v", err) + log.Error(err, "azidentity.NewDefaultAzureCredential") + return err } client := armkeyvault.NewVaultsClient(armcore.NewDefaultConnection(cred, nil), subscriptionID) _, err = client.Get(ctx, resourceGroupName, keyVaultName, nil) if err == nil { - fmt.Printf("INFO: Azure KeyVault (%s) already exists.\n", keyVaultName) + log.Info("Azure KeyVault already exists", "keyVaultName", keyVaultName) return nil } if err != nil && strings.Contains(err.Error(), "ResourceNotFound") { keyVaultNameAvailable, err := client.CheckNameAvailability(ctx, armkeyvault.VaultCheckNameAvailabilityParameters{Name: to.StringPtr(keyVaultName), Type: to.StringPtr("Microsoft.KeyVault/vaults")}, nil) if err != nil { - return fmt.Errorf("Failed Azure/CreateKeyVault/client.CheckNameAvailability: %v", err) + log.Error(err, "client.CheckNameAvailability") + return err } if !*keyVaultNameAvailable.CheckNameAvailabilityResult.NameAvailable { - return fmt.Errorf("Failed Azure/CreateKeyVault/client.CheckNameAvailability: Azure KeyVault Name (%s) not available", keyVaultName) + log.Error(err, "client.CheckNameAvailability: Azure KeyVault Name not available", "keyVaultName", keyVaultName) + return err } poll, err := client.BeginCreateOrUpdate( @@ -184,46 +210,172 @@ func CreateKeyVault(ctx context.Context, resourceGroupName, resourceGroupLocatio }, }, nil) if err != nil { - return fmt.Errorf("Failed Azure/CreateKeyVault/client.BeginCreateOrUpdate: %v", err) + log.Error(err, "client.BeginCreateOrUpdate") + return err } _, err = poll.PollUntilDone(ctx, 5*time.Second) if err != nil { - return fmt.Errorf("Failed Azure/CreateKeyVault/poll.PollUntilDone: %v", err) + log.Error(err, "poll.PollUntilDone") + return err } - fmt.Printf("INFO: Azure KeyVault (%s) created.\n", keyVaultName) + log.Info("Azure KeyVault created", "keyVaultName", keyVaultName) return nil } return fmt.Errorf("Failed Azure/CreateKeyVault/client.Get: %v", err) } +// CreateKeyVaultAccessPolicy creates Azure Key Vault Access Policy (if it doesn't exist) or returns error +func CreateKeyVaultAccessPolicy(ctx context.Context, resourceGroupName, resourceGroupLocation, keyVaultName, subscriptionID, tenantID string) error { + log := logr.FromContext(ctx) + + currentUserObjectID, err := getCurrentUserObjectID(ctx, tenantID) + if err != nil { + log.Error(err, "getCurrentUserObjectID") + return err + } + + cred, err := azidentity.NewDefaultAzureCredential(nil) + if err != nil { + log.Error(err, "azidentity.NewDefaultAzureCredential") + return err + } + + client := armkeyvault.NewVaultsClient(armcore.NewDefaultConnection(cred, nil), subscriptionID) + + keyPermissions := armkeyvault.Permissions{ + Keys: &[]armkeyvault.KeyPermissions{ + armkeyvault.KeyPermissionsUpdate, + armkeyvault.KeyPermissionsGet, + armkeyvault.KeyPermissionsList, + armkeyvault.KeyPermissionsEncrypt, + armkeyvault.KeyPermissionsDecrypt, + }, + } + + accessPolicies := []armkeyvault.AccessPolicyEntry{ + { + TenantID: &tenantID, + ObjectID: ¤tUserObjectID, + Permissions: &keyPermissions, + }, + } + + properties := armkeyvault.VaultAccessPolicyProperties{AccessPolicies: &accessPolicies} + parameters := armkeyvault.VaultAccessPolicyParameters{Properties: &properties} + options := armkeyvault.VaultsUpdateAccessPolicyOptions{} + + kv, err := client.Get(ctx, resourceGroupName, keyVaultName, nil) + if err != nil { + log.Error(err, "client.Get") + return err + } + + // Loop through all access policies + for _, accessPolicy := range *kv.Vault.Properties.AccessPolicies { + // Check if the current object id for the access policy is the same as the current user object id + if *accessPolicy.ObjectID == currentUserObjectID { + // Check if the Key Permissions in the access policy are the same as the required Key Permissions + if keyPermissionsEqual(*accessPolicy.Permissions.Keys, *keyPermissions.Keys) { + // If the correct Key Permissions already exists, return early + log.Info("Azure KeyVault Access Policy already correct", "currentUserObjectID", currentUserObjectID) + return nil + } + } + } + + _, err = client.UpdateAccessPolicy(ctx, resourceGroupName, keyVaultName, armkeyvault.AccessPolicyUpdateKindAdd, parameters, &options) + if err != nil { + log.Error(err, "client.UpdateAccessPolicy") + return err + } + + log.Info("Azure KeyVault Access Policy created or updated", "currentUserObjectID", currentUserObjectID) + + return nil +} + // CreateResourceLock creates Azure Resource Lock (if it doesn't exist) or return error func CreateResourceLock(ctx context.Context, resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, lockName, subscriptionID string) error { + log := logr.FromContext(ctx) + client := locks.NewManagementLocksClient(subscriptionID) authorizer, err := azidext.NewDefaultAzureCredentialAdapter(nil) if err != nil { - return fmt.Errorf("Failed Azure/CreateResourceLock/azidext.NewDefaultAzureCredentialAdapter: %v", err) + log.Error(err, "azidext.NewDefaultAzureCredentialAdapter") + return err } client.Authorizer = authorizer _, err = client.GetAtResourceLevel(ctx, resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, lockName) if err == nil { - fmt.Printf("INFO: Azure Resource Lock (%s/%s/%s/%s) already exists.\n", resourceGroupName, resourceProviderNamespace, resourceType, resourceName) + log.Info("Azure Resource Lock already exists", "resourceGroupName", resourceGroupName, "resourceProviderNamespace", resourceProviderNamespace, "resourceType", resourceType, "resourceName", resourceName) return nil } if err != nil && strings.Contains(err.Error(), "LockNotFound") { _, err = client.CreateOrUpdateAtResourceLevel(ctx, resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, lockName, locks.ManagementLockObject{ManagementLockProperties: &locks.ManagementLockProperties{Level: "CanNotDelete", Notes: to.StringPtr("CanNotDelete")}}) if err != nil { - return fmt.Errorf("Failed Azure/CreateResourceLock/client.CreateOrUpdateAtResourceLevel: %v", err) + log.Error(err, "client.CreateOrUpdateAtResourceLevel") + return err } - fmt.Printf("INFO: Azure Resource Lock (%s/%s/%s/%s) created.\n", resourceGroupName, resourceProviderNamespace, resourceType, resourceName) + log.Info("Azure Resource Lock created", "resourceGroupName", resourceGroupName, "resourceProviderNamespace", resourceProviderNamespace, "resourceType", resourceType, "resourceName", resourceName) return nil } - return fmt.Errorf("Failed Azure/CreateResourceLock/client.GetAtResourceLevel: %v", err) + log.Error(err, "client.GetAtResourceLevel") + return err +} + +func getCurrentUserObjectID(ctx context.Context, tenantID string) (string, error) { + log := logr.FromContext(ctx) + + client := graphrbac.NewSignedInUserClient(tenantID) + + defaultCredential := azidentity.DefaultAzureCredentialOptions{} + tokenRequestOptions := azcore.TokenRequestOptions{Scopes: []string{"https://graph.windows.net/.default"}} + authenticationPolicy := azcore.AuthenticationPolicyOptions{Options: tokenRequestOptions} + credentialOptions := azidext.DefaultAzureCredentialOptions{DefaultCredential: &defaultCredential, AuthenticationPolicy: &authenticationPolicy} + authorizer, err := azidext.NewDefaultAzureCredentialAdapter(&credentialOptions) + if err != nil { + log.Error(err, "azidext.NewDefaultAzureCredentialAdapter") + return "", err + } + + client.Authorizer = authorizer + + currentUser, err := client.Get(ctx) + if err != nil { + log.Error(err, "client.Get") + return "", err + } + + return *currentUser.ObjectID, nil +} + +func keyPermissionsEqual(a, b []armkeyvault.KeyPermissions) bool { + if (a == nil) != (b == nil) { + return false + } + + if len(a) != len(b) { + return false + } + +OUTER: + for _, i := range a { + for _, j := range b { + // a may have the first letters uppercase while b always have them lowercase + if strings.ToLower(string(i)) == strings.ToLower(string(j)) { + continue OUTER + } + } + return false + } + + return true } From 930e482ce4285692853c78329559465b33e5fe77 Mon Sep 17 00:00:00 2001 From: Simon Gottschlag Date: Tue, 22 Dec 2020 16:49:45 +0100 Subject: [PATCH 05/16] Add CreateKeyVaultKey --- docker/go-tf-prepare/main.go | 7 +++- docker/go-tf-prepare/pkg/azure/main.go | 44 ++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/docker/go-tf-prepare/main.go b/docker/go-tf-prepare/main.go index 899ecb2..5dd4f5d 100644 --- a/docker/go-tf-prepare/main.go +++ b/docker/go-tf-prepare/main.go @@ -19,6 +19,7 @@ func main() { storageAccountName := flag.String("storage-account-name", "", "Azure Storage Account Name") storageAccountContainer := flag.String("storage-account-container", "", "Azure Storage Container") keyVaultName := flag.String("keyvault-name", "", "Azure KeyVault Name") + keyVaultKeyName := flag.String("keyvault-key-name", "", "Azure KeyVault Key Name") flag.Parse() stdr.SetVerbosity(1) @@ -62,6 +63,10 @@ func main() { os.Exit(1) } - os.Exit(0) + err = azure.CreateKeyVaultKey(ctx, *resourceGroupName, *keyVaultName, *keyVaultKeyName, *subscriptionID) + if err != nil { + os.Exit(1) + } + os.Exit(0) } diff --git a/docker/go-tf-prepare/pkg/azure/main.go b/docker/go-tf-prepare/pkg/azure/main.go index c776f30..376d1ad 100644 --- a/docker/go-tf-prepare/pkg/azure/main.go +++ b/docker/go-tf-prepare/pkg/azure/main.go @@ -296,6 +296,50 @@ func CreateKeyVaultAccessPolicy(ctx context.Context, resourceGroupName, resource return nil } +// CreateKeyVaultKey creates Azure Key Vault Key (if it doesn't exist) or returns error +func CreateKeyVaultKey(ctx context.Context, resourceGroupName, keyVaultName, keyName, subscriptionID string) error { + log := logr.FromContext(ctx) + + cred, err := azidentity.NewDefaultAzureCredential(nil) + if err != nil { + log.Error(err, "azidentity.NewDefaultAzureCredential") + return err + } + + client := armkeyvault.NewKeysClient(armcore.NewDefaultConnection(cred, nil), subscriptionID) + + _, err = client.Get(ctx, resourceGroupName, keyVaultName, keyName, nil) + if err == nil { + log.Info("Azure KeyVault Key already exists", "keyName", keyName) + return nil + } + + _, err = client.CreateIfNotExist( + ctx, + resourceGroupName, + keyVaultName, + keyName, + armkeyvault.KeyCreateParameters{ + Properties: &armkeyvault.KeyProperties{ + Attributes: &armkeyvault.Attributes{ + Enabled: to.BoolPtr(true), + }, + KeySize: to.Int32Ptr(2048), + KeyOps: &[]armkeyvault.JSONWebKeyOperation{ + armkeyvault.JSONWebKeyOperationEncrypt, + armkeyvault.JSONWebKeyOperationDecrypt, + }, + Kty: armkeyvault.JSONWebKeyTypeRsa.ToPtr(), + }}, nil) + if err != nil { + log.Error(err, "armkeyvault.NewKeysClient") + return err + } + + log.Info("Azure KeyVault Key created", "keyName", keyName) + return nil +} + // CreateResourceLock creates Azure Resource Lock (if it doesn't exist) or return error func CreateResourceLock(ctx context.Context, resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, lockName, subscriptionID string) error { log := logr.FromContext(ctx) From 7134dc0198ffc1856d17780b7c02c8e889c213df Mon Sep 17 00:00:00 2001 From: Simon Gottschlag Date: Tue, 22 Dec 2020 16:51:36 +0100 Subject: [PATCH 06/16] Rename file --- docker/go-tf-prepare/pkg/azure/{main.go => azure.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docker/go-tf-prepare/pkg/azure/{main.go => azure.go} (100%) diff --git a/docker/go-tf-prepare/pkg/azure/main.go b/docker/go-tf-prepare/pkg/azure/azure.go similarity index 100% rename from docker/go-tf-prepare/pkg/azure/main.go rename to docker/go-tf-prepare/pkg/azure/azure.go From d82be0641c67ddba0d98eb7d2a64f6e1a321ac23 Mon Sep 17 00:00:00 2001 From: Simon Gottschlag Date: Tue, 22 Dec 2020 17:31:43 +0100 Subject: [PATCH 07/16] Add create permissions --- docker/go-tf-prepare/pkg/azure/azure.go | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/go-tf-prepare/pkg/azure/azure.go b/docker/go-tf-prepare/pkg/azure/azure.go index 376d1ad..58284ef 100644 --- a/docker/go-tf-prepare/pkg/azure/azure.go +++ b/docker/go-tf-prepare/pkg/azure/azure.go @@ -247,6 +247,7 @@ func CreateKeyVaultAccessPolicy(ctx context.Context, resourceGroupName, resource keyPermissions := armkeyvault.Permissions{ Keys: &[]armkeyvault.KeyPermissions{ armkeyvault.KeyPermissionsUpdate, + armkeyvault.KeyPermissionsCreate, armkeyvault.KeyPermissionsGet, armkeyvault.KeyPermissionsList, armkeyvault.KeyPermissionsEncrypt, From de6d724969fba32a9314c8f1404cceb7c171f78c Mon Sep 17 00:00:00 2001 From: Simon Gottschlag Date: Tue, 22 Dec 2020 17:31:58 +0100 Subject: [PATCH 08/16] Build tf-prepare --- docker/Dockerfile | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 2868676..82a8f13 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,3 +1,12 @@ +FROM golang:1.15 as tf-prepare-builder +WORKDIR /workspace + +COPY ./go-tf-prepare/go.mod ./go-tf-prepare/go.sum ./ +RUN go mod download +COPY ./go-tf-prepare/main.go main.go +COPY ./go-tf-prepare/pkg/ pkg/ +RUN GOOS=linux GOARCH=amd64 GO111MODULE=on go build -o tf-prepare main.go + FROM alpine:3.12.1 ENV USER="tools" @@ -75,6 +84,9 @@ RUN /usr/src/install-scripts/helm.sh --version="v3.4.1" --sha="538f85b4b73ac6160 COPY install-scripts/cleanup.sh /usr/src/install-scripts/cleanup.sh RUN /usr/src/install-scripts/cleanup.sh +COPY --from=tf-prepare-builder /workspace/tf-prepare /usr/local/bin/tf-prepare +RUN chmod +x /usr/local/bin/tf-prepare + RUN rm -rf /tmp/install COPY opa-policies /opt/opa-policies From 37c8352c31694f4589d6b7ba77ca72df01743b10 Mon Sep 17 00:00:00 2001 From: Simon Gottschlag Date: Tue, 22 Dec 2020 17:32:07 +0100 Subject: [PATCH 09/16] Move from bash to tf-prepare --- docker/terraform.sh | 48 +++------------------------------------------ 1 file changed, 3 insertions(+), 45 deletions(-) diff --git a/docker/terraform.sh b/docker/terraform.sh index 70a12f0..e6cfe7d 100755 --- a/docker/terraform.sh +++ b/docker/terraform.sh @@ -21,52 +21,10 @@ if [ -z "${OPA_BLAST_RADIUS}" ]; then OPA_BLAST_RADIUS=50 fi -set_azure_keyvault_permissions() { - echo "Assigning permissions to Azure KeyVault ${BACKEND_KV}" - AZ_ACCOUNT_TYPE="$(az account show --query user.type --output tsv)" - if [[ "${AZ_ACCOUNT_TYPE}" = "user" ]]; then - AZ_USER_OBJECT_ID="$(az ad signed-in-user show --query objectId --output tsv)" - az keyvault set-policy --name ${BACKEND_KV} --resource-group ${BACKEND_RG} --object-id ${AZ_USER_OBJECT_ID} --key-permissions create get list encrypt decrypt 1>/dev/null - elif [[ "${AZ_ACCOUNT_TYPE}" = "servicePrincipal" ]]; then - AZ_SPN="$(az account show --query user.name --output tsv)" - az keyvault set-policy --name ${BACKEND_KV} --resource-group ${BACKEND_RG} --spn ${AZ_SPN} --key-permissions create get list encrypt decrypt 1>/dev/null - fi -} - prepare () { - if [ $(az group exists --name ${BACKEND_RG}) = false ]; then - echo "INFO: Creating resource group ${BACKEND_RG} in location ${RG_LOCATION_LONG}" - az group create --name ${BACKEND_RG} --location ${RG_LOCATION_LONG} - fi - - if ! $(az storage account show --resource-group ${BACKEND_RG} --name ${BACKEND_NAME} --output none); then - echo "Creating Azure Storage Account ${BACKEND_NAME} in location ${RG_LOCATION_LONG} / resource group ${BACKEND_RG}" - az storage account create --resource-group ${BACKEND_RG} --name ${BACKEND_NAME} 1>/dev/null - fi - - if ! $(az storage container show --account-name ${BACKEND_NAME} --name ${CONTAINER_NAME} --output none); then - echo "Creating Azure Storage Container ${CONTAINER_NAME} in Storage Account ${BACKEND_NAME}" - az storage container create --account-name ${BACKEND_NAME} --name ${CONTAINER_NAME} 1>/dev/null - fi - - if ! $(az keyvault show --name ${BACKEND_KV} --output none); then - echo "Creating Azure KeyVault ${BACKEND_KV} in location ${RG_LOCATION_LONG} / resource group ${BACKEND_RG}" - az keyvault create --name ${BACKEND_KV} --resource-group ${BACKEND_RG} --location ${RG_LOCATION_LONG} 1>/dev/null - fi - - set +e - KEYVAULT_KEY_TEST="$(az keyvault key show --vault-name ${BACKEND_KV} --name ${BACKEND_KV_KEY} --output none 2>&1)" - if echo ${KEYVAULT_KEY_TEST} | grep KeyNotFound; then - echo "Creating Azure KeyVault key in ${BACKEND_KV}" - az keyvault key create --name ${BACKEND_KV_KEY} --vault-name ${BACKEND_KV} --protection software --ops encrypt decrypt 1>/dev/null - set_azure_keyvault_permissions - elif echo ${KEYVAULT_KEY_TEST} | grep "does not have keys get permission on key vault"; then - set_azure_keyvault_permissions - fi - set -e - - az lock create --name DoNotDelete --resource-group ${BACKEND_RG} --lock-type CanNotDelete --resource-type Microsoft.Storage/storageAccounts --resource ${BACKEND_NAME} 1>/dev/null - az lock create --name DoNotDelete --resource-group ${BACKEND_RG} --lock-type CanNotDelete --resource-type Microsoft.KeyVault/vaults --resource ${BACKEND_KV} 1>/dev/null + AZURE_SUBSCRIPTION_ID=$(az account show --output tsv --query id) + AZURE_TENANT_ID=$(az account show --output tsv --query tenantId) + tf-prepare --resource-group-name="${BACKEND_RG}" --resource-group-location="${RG_LOCATION_LONG}" --subscription-id="${AZURE_SUBSCRIPTION_ID}" --tenant-id="${AZURE_TENANT_ID}" --storage-account-name="${BACKEND_NAME}" --storage-account-container="${CONTAINER_NAME}" --keyvault-name="${BACKEND_KV}" --keyvault-key-name="${BACKEND_KV_KEY}" } plan () { From 5e9634001da63f14a303dfebc35352b0d3811261 Mon Sep 17 00:00:00 2001 From: Simon Gottschlag Date: Tue, 22 Dec 2020 21:28:03 +0100 Subject: [PATCH 10/16] Update storage account configuration --- docker/go-tf-prepare/pkg/azure/azure.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docker/go-tf-prepare/pkg/azure/azure.go b/docker/go-tf-prepare/pkg/azure/azure.go index 58284ef..bda08e0 100644 --- a/docker/go-tf-prepare/pkg/azure/azure.go +++ b/docker/go-tf-prepare/pkg/azure/azure.go @@ -97,10 +97,12 @@ func CreateStorageAccount(ctx context.Context, resourceGroupName, resourceGroupL Name: armstorage.SKUNameStandardGrs.ToPtr(), Tier: armstorage.SKUTierStandard.ToPtr(), }, - Kind: armstorage.KindBlobStorage.ToPtr(), + Kind: armstorage.KindStorageV2.ToPtr(), Location: to.StringPtr(resourceGroupLocation), Properties: &armstorage.StorageAccountPropertiesCreateParameters{ - AccessTier: armstorage.AccessTierCool.ToPtr(), + AccessTier: armstorage.AccessTierHot.ToPtr(), + AllowBlobPublicAccess: to.BoolPtr(false), + MinimumTLSVersion: armstorage.MinimumTLSVersionTLS12.ToPtr(), }, }, nil) From e351038a010d6b5ae31798abda69f3a3d35726e8 Mon Sep 17 00:00:00 2001 From: Simon Gottschlag Date: Tue, 22 Dec 2020 21:57:17 +0100 Subject: [PATCH 11/16] Change from pflag to urfave/cli --- docker/go-tf-prepare/go.mod | 2 +- docker/go-tf-prepare/go.sum | 13 +++- docker/go-tf-prepare/main.go | 129 +++++++++++++++++++++++++++-------- 3 files changed, 114 insertions(+), 30 deletions(-) diff --git a/docker/go-tf-prepare/go.mod b/docker/go-tf-prepare/go.mod index 7149057..3b37ad2 100644 --- a/docker/go-tf-prepare/go.mod +++ b/docker/go-tf-prepare/go.mod @@ -14,5 +14,5 @@ require ( github.com/go-logr/logr v0.3.0 github.com/go-logr/stdr v0.3.0 github.com/jongio/azidext/go/azidext v0.1.0 - github.com/spf13/pflag v1.0.5 + github.com/urfave/cli/v2 v2.3.0 ) diff --git a/docker/go-tf-prepare/go.sum b/docker/go-tf-prepare/go.sum index 77662cc..8cf4017 100644 --- a/docker/go-tf-prepare/go.sum +++ b/docker/go-tf-prepare/go.sum @@ -40,6 +40,9 @@ github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8 github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= @@ -56,11 +59,15 @@ github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7T github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -79,3 +86,5 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/docker/go-tf-prepare/main.go b/docker/go-tf-prepare/main.go index 5dd4f5d..d69010c 100644 --- a/docker/go-tf-prepare/main.go +++ b/docker/go-tf-prepare/main.go @@ -7,66 +7,141 @@ import ( "github.com/go-logr/logr" "github.com/go-logr/stdr" - flag "github.com/spf13/pflag" + "github.com/urfave/cli/v2" "github.com/xenitab/github-actions/docker/go-tf-prepare/pkg/azure" ) func main() { - subscriptionID := flag.String("subscription-id", "", "Azure Subscription ID") - tenantID := flag.String("tenant-id", "", "Azure Tenant ID") - resourceGroupName := flag.String("resource-group-name", "", "Azure Resource Group Name") - resourceGroupLocation := flag.String("resource-group-location", "", "Azure Resource Group Location") - storageAccountName := flag.String("storage-account-name", "", "Azure Storage Account Name") - storageAccountContainer := flag.String("storage-account-container", "", "Azure Storage Container") - keyVaultName := flag.String("keyvault-name", "", "Azure KeyVault Name") - keyVaultKeyName := flag.String("keyvault-key-name", "", "Azure KeyVault Key Name") - flag.Parse() - stdr.SetVerbosity(1) log := stdr.New(stdlog.New(os.Stderr, "", stdlog.LstdFlags|stdlog.Lshortfile)) log = log.WithName("go-tf-preparer") ctx := logr.NewContext(context.Background(), log) - err := azure.CreateResourceGroup(ctx, *resourceGroupName, *resourceGroupLocation, *subscriptionID) + app := &cli.App{ + Commands: []*cli.Command{ + { + Name: "azure", + Usage: "Terraform prepare for Azure", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "subscription-id", + Usage: "Azure Subscription ID", + Required: true, + EnvVars: []string{"AZURE_SUBSCRIPTION_ID"}, + }, + &cli.StringFlag{ + Name: "tenant-id", + Usage: "Azure Tenant ID", + Required: true, + EnvVars: []string{"AZURE_TENANT_ID"}, + }, + &cli.StringFlag{ + Name: "resource-group-name", + Usage: "Azure Resource Group Name", + Required: true, + EnvVars: []string{"AZURE_RESOURCE_GROUP_NAME"}, + }, + &cli.StringFlag{ + Name: "resource-group-location", + Usage: "Azure Resource Group Location", + Required: true, + EnvVars: []string{"AZURE_RESOURCE_GROUP_LOCATION"}, + }, + &cli.StringFlag{ + Name: "storage-account-name", + Usage: "Azure Storage Account Name", + Required: true, + EnvVars: []string{"AZURE_STORAGE_ACCOUNT_NAME"}, + }, + &cli.StringFlag{ + Name: "storage-account-container", + Usage: "Azure Storage Account Container", + Required: true, + EnvVars: []string{"AZURE_STORAGE_ACCOUNT_CONTAINER"}, + }, + &cli.StringFlag{ + Name: "keyvault-name", + Usage: "Azure KeyVault Name", + Required: true, + EnvVars: []string{"AZURE_KEYVAULT_NAME"}, + }, + &cli.StringFlag{ + Name: "keyvault-key-name", + Usage: "Azure KeyVault Key Name", + Required: true, + EnvVars: []string{"AZURE_KEYVAULT_KEY_NAME"}, + }, + }, + Action: func(cli *cli.Context) error { + err := azureAction(ctx, cli) + if err != nil { + return err + } + return nil + }, + }, + }, + } + + err := app.Run(os.Args) if err != nil { + log.Error(err, "CLI execution failed") os.Exit(1) } - err = azure.CreateStorageAccount(ctx, *resourceGroupName, *resourceGroupLocation, *storageAccountName, *subscriptionID) + os.Exit(0) +} + +func azureAction(ctx context.Context, cli *cli.Context) error { + subscriptionID := cli.String("subscription-id") + tenantID := cli.String("tenant-id") + resourceGroupName := cli.String("resource-group-name") + resourceGroupLocation := cli.String("resource-group-location") + storageAccountName := cli.String("storage-account-name") + storageAccountContainer := cli.String("storage-account-container") + keyVaultName := cli.String("keyvault-name") + keyVaultKeyName := cli.String("keyvault-key-name") + + err := azure.CreateResourceGroup(ctx, resourceGroupName, resourceGroupLocation, subscriptionID) if err != nil { - os.Exit(1) + return err } - err = azure.CreateResourceLock(ctx, *resourceGroupName, "Microsoft.Storage", "", "storageAccounts", *storageAccountName, "DoNotDelete", *subscriptionID) + err = azure.CreateStorageAccount(ctx, resourceGroupName, resourceGroupLocation, storageAccountName, subscriptionID) if err != nil { - os.Exit(1) + return err } - err = azure.CreateStorageAccountContainer(ctx, *resourceGroupName, *storageAccountName, *storageAccountContainer, *subscriptionID) + err = azure.CreateResourceLock(ctx, resourceGroupName, "Microsoft.Storage", "", "storageAccounts", storageAccountName, "DoNotDelete", subscriptionID) if err != nil { - os.Exit(1) + return err } - err = azure.CreateKeyVault(ctx, *resourceGroupName, *resourceGroupLocation, *keyVaultName, *subscriptionID, *tenantID) + err = azure.CreateStorageAccountContainer(ctx, resourceGroupName, storageAccountName, storageAccountContainer, subscriptionID) if err != nil { - os.Exit(1) + return err } - err = azure.CreateResourceLock(ctx, *resourceGroupName, "Microsoft.KeyVault", "", "vaults", *keyVaultName, "DoNotDelete", *subscriptionID) + err = azure.CreateKeyVault(ctx, resourceGroupName, resourceGroupLocation, keyVaultName, subscriptionID, tenantID) if err != nil { - os.Exit(1) + return err } - err = azure.CreateKeyVaultAccessPolicy(ctx, *resourceGroupName, *resourceGroupLocation, *keyVaultName, *subscriptionID, *tenantID) + err = azure.CreateResourceLock(ctx, resourceGroupName, "Microsoft.KeyVault", "", "vaults", keyVaultName, "DoNotDelete", subscriptionID) if err != nil { - os.Exit(1) + return err } - err = azure.CreateKeyVaultKey(ctx, *resourceGroupName, *keyVaultName, *keyVaultKeyName, *subscriptionID) + err = azure.CreateKeyVaultAccessPolicy(ctx, resourceGroupName, resourceGroupLocation, keyVaultName, subscriptionID, tenantID) if err != nil { - os.Exit(1) + return err } - os.Exit(0) + err = azure.CreateKeyVaultKey(ctx, resourceGroupName, keyVaultName, keyVaultKeyName, subscriptionID) + if err != nil { + return err + } + + return nil } From 3450fbe5b9ea99ff22cc669a13032128d2b7cf36 Mon Sep 17 00:00:00 2001 From: Simon Gottschlag Date: Tue, 22 Dec 2020 22:00:29 +0100 Subject: [PATCH 12/16] Move azure flags --- docker/go-tf-prepare/main.go | 105 ++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 50 deletions(-) diff --git a/docker/go-tf-prepare/main.go b/docker/go-tf-prepare/main.go index d69010c..f21ec2b 100644 --- a/docker/go-tf-prepare/main.go +++ b/docker/go-tf-prepare/main.go @@ -23,56 +23,7 @@ func main() { { Name: "azure", Usage: "Terraform prepare for Azure", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "subscription-id", - Usage: "Azure Subscription ID", - Required: true, - EnvVars: []string{"AZURE_SUBSCRIPTION_ID"}, - }, - &cli.StringFlag{ - Name: "tenant-id", - Usage: "Azure Tenant ID", - Required: true, - EnvVars: []string{"AZURE_TENANT_ID"}, - }, - &cli.StringFlag{ - Name: "resource-group-name", - Usage: "Azure Resource Group Name", - Required: true, - EnvVars: []string{"AZURE_RESOURCE_GROUP_NAME"}, - }, - &cli.StringFlag{ - Name: "resource-group-location", - Usage: "Azure Resource Group Location", - Required: true, - EnvVars: []string{"AZURE_RESOURCE_GROUP_LOCATION"}, - }, - &cli.StringFlag{ - Name: "storage-account-name", - Usage: "Azure Storage Account Name", - Required: true, - EnvVars: []string{"AZURE_STORAGE_ACCOUNT_NAME"}, - }, - &cli.StringFlag{ - Name: "storage-account-container", - Usage: "Azure Storage Account Container", - Required: true, - EnvVars: []string{"AZURE_STORAGE_ACCOUNT_CONTAINER"}, - }, - &cli.StringFlag{ - Name: "keyvault-name", - Usage: "Azure KeyVault Name", - Required: true, - EnvVars: []string{"AZURE_KEYVAULT_NAME"}, - }, - &cli.StringFlag{ - Name: "keyvault-key-name", - Usage: "Azure KeyVault Key Name", - Required: true, - EnvVars: []string{"AZURE_KEYVAULT_KEY_NAME"}, - }, - }, + Flags: azureFlags(), Action: func(cli *cli.Context) error { err := azureAction(ctx, cli) if err != nil { @@ -145,3 +96,57 @@ func azureAction(ctx context.Context, cli *cli.Context) error { return nil } + +func azureFlags() []cli.Flag { + flags := []cli.Flag{ + &cli.StringFlag{ + Name: "subscription-id", + Usage: "Azure Subscription ID", + Required: true, + EnvVars: []string{"AZURE_SUBSCRIPTION_ID"}, + }, + &cli.StringFlag{ + Name: "tenant-id", + Usage: "Azure Tenant ID", + Required: true, + EnvVars: []string{"AZURE_TENANT_ID"}, + }, + &cli.StringFlag{ + Name: "resource-group-name", + Usage: "Azure Resource Group Name", + Required: true, + EnvVars: []string{"AZURE_RESOURCE_GROUP_NAME"}, + }, + &cli.StringFlag{ + Name: "resource-group-location", + Usage: "Azure Resource Group Location", + Required: true, + EnvVars: []string{"AZURE_RESOURCE_GROUP_LOCATION"}, + }, + &cli.StringFlag{ + Name: "storage-account-name", + Usage: "Azure Storage Account Name", + Required: true, + EnvVars: []string{"AZURE_STORAGE_ACCOUNT_NAME"}, + }, + &cli.StringFlag{ + Name: "storage-account-container", + Usage: "Azure Storage Account Container", + Required: true, + EnvVars: []string{"AZURE_STORAGE_ACCOUNT_CONTAINER"}, + }, + &cli.StringFlag{ + Name: "keyvault-name", + Usage: "Azure KeyVault Name", + Required: true, + EnvVars: []string{"AZURE_KEYVAULT_NAME"}, + }, + &cli.StringFlag{ + Name: "keyvault-key-name", + Usage: "Azure KeyVault Key Name", + Required: true, + EnvVars: []string{"AZURE_KEYVAULT_KEY_NAME"}, + }, + } + return flags +} From f320fdad5650d9933b6fbbd3628c6d530276ba70 Mon Sep 17 00:00:00 2001 From: Simon Gottschlag Date: Tue, 22 Dec 2020 22:04:15 +0100 Subject: [PATCH 13/16] Make resource locks configurable --- docker/go-tf-prepare/main.go | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/docker/go-tf-prepare/main.go b/docker/go-tf-prepare/main.go index f21ec2b..3d59b43 100644 --- a/docker/go-tf-prepare/main.go +++ b/docker/go-tf-prepare/main.go @@ -53,6 +53,7 @@ func azureAction(ctx context.Context, cli *cli.Context) error { storageAccountContainer := cli.String("storage-account-container") keyVaultName := cli.String("keyvault-name") keyVaultKeyName := cli.String("keyvault-key-name") + resourceLocks := cli.Bool("resource-locks") err := azure.CreateResourceGroup(ctx, resourceGroupName, resourceGroupLocation, subscriptionID) if err != nil { @@ -64,9 +65,11 @@ func azureAction(ctx context.Context, cli *cli.Context) error { return err } - err = azure.CreateResourceLock(ctx, resourceGroupName, "Microsoft.Storage", "", "storageAccounts", storageAccountName, "DoNotDelete", subscriptionID) - if err != nil { - return err + if resourceLocks { + err = azure.CreateResourceLock(ctx, resourceGroupName, "Microsoft.Storage", "", "storageAccounts", storageAccountName, "DoNotDelete", subscriptionID) + if err != nil { + return err + } } err = azure.CreateStorageAccountContainer(ctx, resourceGroupName, storageAccountName, storageAccountContainer, subscriptionID) @@ -79,9 +82,11 @@ func azureAction(ctx context.Context, cli *cli.Context) error { return err } - err = azure.CreateResourceLock(ctx, resourceGroupName, "Microsoft.KeyVault", "", "vaults", keyVaultName, "DoNotDelete", subscriptionID) - if err != nil { - return err + if resourceLocks { + err = azure.CreateResourceLock(ctx, resourceGroupName, "Microsoft.KeyVault", "", "vaults", keyVaultName, "DoNotDelete", subscriptionID) + if err != nil { + return err + } } err = azure.CreateKeyVaultAccessPolicy(ctx, resourceGroupName, resourceGroupLocation, keyVaultName, subscriptionID, tenantID) @@ -147,6 +152,12 @@ func azureFlags() []cli.Flag { Required: true, EnvVars: []string{"AZURE_KEYVAULT_KEY_NAME"}, }, + &cli.BoolFlag{ + Name: "resource-locks", + Usage: "Should Azure Resource Locks be used?", + Value: true, + EnvVars: []string{"AZURE_RESOURCE_LOCKS"}, + }, } return flags } From 132fd0b5c28f4130bf56ddc68ecf62bbb973a228 Mon Sep 17 00:00:00 2001 From: Simon Gottschlag Date: Tue, 22 Dec 2020 22:53:09 +0100 Subject: [PATCH 14/16] Add support for service principal --- docker/go-tf-prepare/main.go | 9 ++++++++- docker/go-tf-prepare/pkg/azure/azure.go | 17 ++++++++++++----- docker/terraform.sh | 16 +++++++++++++--- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/docker/go-tf-prepare/main.go b/docker/go-tf-prepare/main.go index 3d59b43..bb453e7 100644 --- a/docker/go-tf-prepare/main.go +++ b/docker/go-tf-prepare/main.go @@ -45,6 +45,7 @@ func main() { } func azureAction(ctx context.Context, cli *cli.Context) error { + servicePrincipalObjectID := cli.String("service-principal-object-id") subscriptionID := cli.String("subscription-id") tenantID := cli.String("tenant-id") resourceGroupName := cli.String("resource-group-name") @@ -89,7 +90,7 @@ func azureAction(ctx context.Context, cli *cli.Context) error { } } - err = azure.CreateKeyVaultAccessPolicy(ctx, resourceGroupName, resourceGroupLocation, keyVaultName, subscriptionID, tenantID) + err = azure.CreateKeyVaultAccessPolicy(ctx, resourceGroupName, resourceGroupLocation, keyVaultName, subscriptionID, tenantID, servicePrincipalObjectID) if err != nil { return err } @@ -104,6 +105,12 @@ func azureAction(ctx context.Context, cli *cli.Context) error { func azureFlags() []cli.Flag { flags := []cli.Flag{ + &cli.StringFlag{ + Name: "service-principal-object-id", + Usage: "Service Principal Object ID", + Required: false, + EnvVars: []string{"AZURE_SERVICE_PRINCIPAL_OBJECT_ID"}, + }, &cli.StringFlag{ Name: "subscription-id", Usage: "Azure Subscription ID", diff --git a/docker/go-tf-prepare/pkg/azure/azure.go b/docker/go-tf-prepare/pkg/azure/azure.go index bda08e0..08507c7 100644 --- a/docker/go-tf-prepare/pkg/azure/azure.go +++ b/docker/go-tf-prepare/pkg/azure/azure.go @@ -229,13 +229,20 @@ func CreateKeyVault(ctx context.Context, resourceGroupName, resourceGroupLocatio } // CreateKeyVaultAccessPolicy creates Azure Key Vault Access Policy (if it doesn't exist) or returns error -func CreateKeyVaultAccessPolicy(ctx context.Context, resourceGroupName, resourceGroupLocation, keyVaultName, subscriptionID, tenantID string) error { +func CreateKeyVaultAccessPolicy(ctx context.Context, resourceGroupName, resourceGroupLocation, keyVaultName, subscriptionID, tenantID, servicePrincipalObjectID string) error { log := logr.FromContext(ctx) - currentUserObjectID, err := getCurrentUserObjectID(ctx, tenantID) - if err != nil { - log.Error(err, "getCurrentUserObjectID") - return err + var currentUserObjectID string + if servicePrincipalObjectID == "" { + var err error + currentUserObjectID, err = getCurrentUserObjectID(ctx, tenantID) + if err != nil { + log.Error(err, "getCurrentUserObjectID") + return err + } + } + if servicePrincipalObjectID != "" { + currentUserObjectID = servicePrincipalObjectID } cred, err := azidentity.NewDefaultAzureCredential(nil) diff --git a/docker/terraform.sh b/docker/terraform.sh index e6cfe7d..2f34e9d 100755 --- a/docker/terraform.sh +++ b/docker/terraform.sh @@ -22,9 +22,19 @@ if [ -z "${OPA_BLAST_RADIUS}" ]; then fi prepare () { - AZURE_SUBSCRIPTION_ID=$(az account show --output tsv --query id) - AZURE_TENANT_ID=$(az account show --output tsv --query tenantId) - tf-prepare --resource-group-name="${BACKEND_RG}" --resource-group-location="${RG_LOCATION_LONG}" --subscription-id="${AZURE_SUBSCRIPTION_ID}" --tenant-id="${AZURE_TENANT_ID}" --storage-account-name="${BACKEND_NAME}" --storage-account-container="${CONTAINER_NAME}" --keyvault-name="${BACKEND_KV}" --keyvault-key-name="${BACKEND_KV_KEY}" + AZ_ACCOUNT_TYPE="$(az account show --query user.type --output tsv)" + if [[ "${AZ_ACCOUNT_TYPE}" = "servicePrincipal" ]]; then + export AZURE_SERVICE_PRINCIPAL_OBJECT_ID="$(az account show --query user.name --output tsv)" + fi + export AZURE_SUBSCRIPTION_ID=$(az account show --output tsv --query id) + export AZURE_TENANT_ID=$(az account show --output tsv --query tenantId) + export AZURE_RESOURCE_GROUP_NAME="${BACKEND_RG}" + export AZURE_RESOURCE_GROUP_LOCATION="${RG_LOCATION_LONG}" + export AZURE_STORAGE_ACCOUNT_NAME="${BACKEND_NAME}" + export AZURE_STORAGE_ACCOUNT_CONTAINER="${CONTAINER_NAME}" + export AZURE_KEYVAULT_NAME="${BACKEND_KV}" + export AZURE_KEYVAULT_KEY_NAME="${BACKEND_KV_KEY}" + tf-prepare azure } plan () { From 297061033ee5d4b9d9d502aa170019e7c2d7d8ed Mon Sep 17 00:00:00 2001 From: Simon Gottschlag Date: Wed, 23 Dec 2020 09:45:41 +0100 Subject: [PATCH 15/16] move azure action and flags --- docker/go-tf-prepare/main.go | 131 +------------------ docker/go-tf-prepare/pkg/azure/azure_cli.go | 134 ++++++++++++++++++++ 2 files changed, 137 insertions(+), 128 deletions(-) create mode 100644 docker/go-tf-prepare/pkg/azure/azure_cli.go diff --git a/docker/go-tf-prepare/main.go b/docker/go-tf-prepare/main.go index bb453e7..381f24d 100644 --- a/docker/go-tf-prepare/main.go +++ b/docker/go-tf-prepare/main.go @@ -14,7 +14,7 @@ import ( func main() { stdr.SetVerbosity(1) log := stdr.New(stdlog.New(os.Stderr, "", stdlog.LstdFlags|stdlog.Lshortfile)) - log = log.WithName("go-tf-preparer") + log = log.WithName("tf-prepare") ctx := logr.NewContext(context.Background(), log) @@ -23,9 +23,9 @@ func main() { { Name: "azure", Usage: "Terraform prepare for Azure", - Flags: azureFlags(), + Flags: azure.Flags(), Action: func(cli *cli.Context) error { - err := azureAction(ctx, cli) + err := azure.Action(ctx, cli) if err != nil { return err } @@ -43,128 +43,3 @@ func main() { os.Exit(0) } - -func azureAction(ctx context.Context, cli *cli.Context) error { - servicePrincipalObjectID := cli.String("service-principal-object-id") - subscriptionID := cli.String("subscription-id") - tenantID := cli.String("tenant-id") - resourceGroupName := cli.String("resource-group-name") - resourceGroupLocation := cli.String("resource-group-location") - storageAccountName := cli.String("storage-account-name") - storageAccountContainer := cli.String("storage-account-container") - keyVaultName := cli.String("keyvault-name") - keyVaultKeyName := cli.String("keyvault-key-name") - resourceLocks := cli.Bool("resource-locks") - - err := azure.CreateResourceGroup(ctx, resourceGroupName, resourceGroupLocation, subscriptionID) - if err != nil { - return err - } - - err = azure.CreateStorageAccount(ctx, resourceGroupName, resourceGroupLocation, storageAccountName, subscriptionID) - if err != nil { - return err - } - - if resourceLocks { - err = azure.CreateResourceLock(ctx, resourceGroupName, "Microsoft.Storage", "", "storageAccounts", storageAccountName, "DoNotDelete", subscriptionID) - if err != nil { - return err - } - } - - err = azure.CreateStorageAccountContainer(ctx, resourceGroupName, storageAccountName, storageAccountContainer, subscriptionID) - if err != nil { - return err - } - - err = azure.CreateKeyVault(ctx, resourceGroupName, resourceGroupLocation, keyVaultName, subscriptionID, tenantID) - if err != nil { - return err - } - - if resourceLocks { - err = azure.CreateResourceLock(ctx, resourceGroupName, "Microsoft.KeyVault", "", "vaults", keyVaultName, "DoNotDelete", subscriptionID) - if err != nil { - return err - } - } - - err = azure.CreateKeyVaultAccessPolicy(ctx, resourceGroupName, resourceGroupLocation, keyVaultName, subscriptionID, tenantID, servicePrincipalObjectID) - if err != nil { - return err - } - - err = azure.CreateKeyVaultKey(ctx, resourceGroupName, keyVaultName, keyVaultKeyName, subscriptionID) - if err != nil { - return err - } - - return nil -} - -func azureFlags() []cli.Flag { - flags := []cli.Flag{ - &cli.StringFlag{ - Name: "service-principal-object-id", - Usage: "Service Principal Object ID", - Required: false, - EnvVars: []string{"AZURE_SERVICE_PRINCIPAL_OBJECT_ID"}, - }, - &cli.StringFlag{ - Name: "subscription-id", - Usage: "Azure Subscription ID", - Required: true, - EnvVars: []string{"AZURE_SUBSCRIPTION_ID"}, - }, - &cli.StringFlag{ - Name: "tenant-id", - Usage: "Azure Tenant ID", - Required: true, - EnvVars: []string{"AZURE_TENANT_ID"}, - }, - &cli.StringFlag{ - Name: "resource-group-name", - Usage: "Azure Resource Group Name", - Required: true, - EnvVars: []string{"AZURE_RESOURCE_GROUP_NAME"}, - }, - &cli.StringFlag{ - Name: "resource-group-location", - Usage: "Azure Resource Group Location", - Required: true, - EnvVars: []string{"AZURE_RESOURCE_GROUP_LOCATION"}, - }, - &cli.StringFlag{ - Name: "storage-account-name", - Usage: "Azure Storage Account Name", - Required: true, - EnvVars: []string{"AZURE_STORAGE_ACCOUNT_NAME"}, - }, - &cli.StringFlag{ - Name: "storage-account-container", - Usage: "Azure Storage Account Container", - Required: true, - EnvVars: []string{"AZURE_STORAGE_ACCOUNT_CONTAINER"}, - }, - &cli.StringFlag{ - Name: "keyvault-name", - Usage: "Azure KeyVault Name", - Required: true, - EnvVars: []string{"AZURE_KEYVAULT_NAME"}, - }, - &cli.StringFlag{ - Name: "keyvault-key-name", - Usage: "Azure KeyVault Key Name", - Required: true, - EnvVars: []string{"AZURE_KEYVAULT_KEY_NAME"}, - }, - &cli.BoolFlag{ - Name: "resource-locks", - Usage: "Should Azure Resource Locks be used?", - Value: true, - EnvVars: []string{"AZURE_RESOURCE_LOCKS"}, - }, - } - return flags -} diff --git a/docker/go-tf-prepare/pkg/azure/azure_cli.go b/docker/go-tf-prepare/pkg/azure/azure_cli.go new file mode 100644 index 0000000..f170599 --- /dev/null +++ b/docker/go-tf-prepare/pkg/azure/azure_cli.go @@ -0,0 +1,134 @@ +package azure + +import ( + "context" + + "github.com/urfave/cli/v2" +) + +// Action executes the Azure action +func Action(ctx context.Context, cli *cli.Context) error { + servicePrincipalObjectID := cli.String("service-principal-object-id") + subscriptionID := cli.String("subscription-id") + tenantID := cli.String("tenant-id") + resourceGroupName := cli.String("resource-group-name") + resourceGroupLocation := cli.String("resource-group-location") + storageAccountName := cli.String("storage-account-name") + storageAccountContainer := cli.String("storage-account-container") + keyVaultName := cli.String("keyvault-name") + keyVaultKeyName := cli.String("keyvault-key-name") + resourceLocks := cli.Bool("resource-locks") + + err := CreateResourceGroup(ctx, resourceGroupName, resourceGroupLocation, subscriptionID) + if err != nil { + return err + } + + err = CreateStorageAccount(ctx, resourceGroupName, resourceGroupLocation, storageAccountName, subscriptionID) + if err != nil { + return err + } + + if resourceLocks { + err = CreateResourceLock(ctx, resourceGroupName, "Microsoft.Storage", "", "storageAccounts", storageAccountName, "DoNotDelete", subscriptionID) + if err != nil { + return err + } + } + + err = CreateStorageAccountContainer(ctx, resourceGroupName, storageAccountName, storageAccountContainer, subscriptionID) + if err != nil { + return err + } + + err = CreateKeyVault(ctx, resourceGroupName, resourceGroupLocation, keyVaultName, subscriptionID, tenantID) + if err != nil { + return err + } + + if resourceLocks { + err = CreateResourceLock(ctx, resourceGroupName, "Microsoft.KeyVault", "", "vaults", keyVaultName, "DoNotDelete", subscriptionID) + if err != nil { + return err + } + } + + err = CreateKeyVaultAccessPolicy(ctx, resourceGroupName, resourceGroupLocation, keyVaultName, subscriptionID, tenantID, servicePrincipalObjectID) + if err != nil { + return err + } + + err = CreateKeyVaultKey(ctx, resourceGroupName, keyVaultName, keyVaultKeyName, subscriptionID) + if err != nil { + return err + } + + return nil +} + +// Flags returns the cli flags for Azure +func Flags() []cli.Flag { + flags := []cli.Flag{ + &cli.StringFlag{ + Name: "service-principal-object-id", + Usage: "Service Principal Object ID", + Required: false, + EnvVars: []string{"AZURE_SERVICE_PRINCIPAL_OBJECT_ID"}, + }, + &cli.StringFlag{ + Name: "subscription-id", + Usage: "Azure Subscription ID", + Required: true, + EnvVars: []string{"AZURE_SUBSCRIPTION_ID"}, + }, + &cli.StringFlag{ + Name: "tenant-id", + Usage: "Azure Tenant ID", + Required: true, + EnvVars: []string{"AZURE_TENANT_ID"}, + }, + &cli.StringFlag{ + Name: "resource-group-name", + Usage: "Azure Resource Group Name", + Required: true, + EnvVars: []string{"AZURE_RESOURCE_GROUP_NAME"}, + }, + &cli.StringFlag{ + Name: "resource-group-location", + Usage: "Azure Resource Group Location", + Required: true, + EnvVars: []string{"AZURE_RESOURCE_GROUP_LOCATION"}, + }, + &cli.StringFlag{ + Name: "storage-account-name", + Usage: "Azure Storage Account Name", + Required: true, + EnvVars: []string{"AZURE_STORAGE_ACCOUNT_NAME"}, + }, + &cli.StringFlag{ + Name: "storage-account-container", + Usage: "Azure Storage Account Container", + Required: true, + EnvVars: []string{"AZURE_STORAGE_ACCOUNT_CONTAINER"}, + }, + &cli.StringFlag{ + Name: "keyvault-name", + Usage: "Azure KeyVault Name", + Required: true, + EnvVars: []string{"AZURE_KEYVAULT_NAME"}, + }, + &cli.StringFlag{ + Name: "keyvault-key-name", + Usage: "Azure KeyVault Key Name", + Required: true, + EnvVars: []string{"AZURE_KEYVAULT_KEY_NAME"}, + }, + &cli.BoolFlag{ + Name: "resource-locks", + Usage: "Should Azure Resource Locks be used?", + Value: true, + EnvVars: []string{"AZURE_RESOURCE_LOCKS"}, + }, + } + return flags +} From 2bade9a1bb0a84aedc368accbd3f6f303ee37aad Mon Sep 17 00:00:00 2001 From: Simon Gottschlag Date: Wed, 23 Dec 2020 09:53:14 +0100 Subject: [PATCH 16/16] Add AZURE_RESOURCE_LOCKS and set to true by default --- docker/terraform.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/terraform.sh b/docker/terraform.sh index 2f34e9d..4c7c416 100755 --- a/docker/terraform.sh +++ b/docker/terraform.sh @@ -34,6 +34,7 @@ prepare () { export AZURE_STORAGE_ACCOUNT_CONTAINER="${CONTAINER_NAME}" export AZURE_KEYVAULT_NAME="${BACKEND_KV}" export AZURE_KEYVAULT_KEY_NAME="${BACKEND_KV_KEY}" + export AZURE_RESOURCE_LOCKS="${AZURE_RESOURCE_LOCKS:-true}" tf-prepare azure }