Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

New Resource: azurerm_bastion_host #4096

Merged
merged 24 commits into from
Oct 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
41727bf
initial changes
DanielMabbett Aug 15, 2019
28058d6
go fmt
DanielMabbett Aug 15, 2019
6e01472
adding tests and links for website
DanielMabbett Aug 15, 2019
689269b
update markdown bastion host
DanielMabbett Aug 15, 2019
09eb3a2
remove incompatible and unrequired files/folders
DanielMabbett Aug 15, 2019
78a5dfe
add dns_name argument for bastion host
DanielMabbett Aug 15, 2019
155cb39
cleanup
DanielMabbett Aug 15, 2019
f5a752b
cleanup bastion host test
DanielMabbett Aug 15, 2019
1edfd63
cleanup bastion host resource
DanielMabbett Aug 15, 2019
f15620c
Seperate Bastion into Networking preview api
DanielMabbett Aug 15, 2019
016c73e
remove non-useful comments
DanielMabbett Aug 15, 2019
4ca2e3a
Merge branch 'master' into feature/azure-bastion-host
DanielMabbett Aug 16, 2019
3d8f633
Merge branch 'master' into feature/azure-bastion-host
DanielMabbett Aug 20, 2019
29007a9
Update go.mod
DanielMabbett Aug 20, 2019
21f37e7
bastion: add expand and flatten for ipconfig. Fix typos and errs. Add…
DanielMabbett Oct 3, 2019
6da56d0
init
DanielMabbett Oct 7, 2019
8aeb4c7
working tests. Notes for Bastion Markdown
DanielMabbett Oct 7, 2019
1bd394d
Merge branch 'master' of https://github.com/terraform-providers/terra…
DanielMabbett Oct 7, 2019
b467b95
Merge branch 'feature/azure-bastion-host' into feature/azure-bastion-…
DanielMabbett Oct 7, 2019
35f6880
Merge pull request #1 from DanielMabbett/feature/azure-bastion-host-a…
DanielMabbett Oct 7, 2019
8efb4ef
update with pull from upstream
DanielMabbett Oct 7, 2019
3765ff8
go fmt provider.go for travis ci err
DanielMabbett Oct 8, 2019
9d4326d
move dns_name, expandipconfig pointer return, import test add, rename…
DanielMabbett Oct 9, 2019
8c26237
remove whitespace
DanielMabbett Oct 10, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions azurerm/internal/services/network/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type Client struct {
ApplicationGatewaysClient *network.ApplicationGatewaysClient
ApplicationSecurityGroupsClient *network.ApplicationSecurityGroupsClient
AzureFirewallsClient *network.AzureFirewallsClient
BastionHostsClient *network.BastionHostsClient
ConnectionMonitorsClient *network.ConnectionMonitorsClient
DDOSProtectionPlansClient *network.DdosProtectionPlansClient
ExpressRouteAuthsClient *network.ExpressRouteCircuitAuthorizationsClient
Expand Down Expand Up @@ -45,6 +46,9 @@ func BuildClient(o *common.ClientOptions) *Client {
AzureFirewallsClient := network.NewAzureFirewallsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&AzureFirewallsClient.Client, o.ResourceManagerAuthorizer)

BastionHostsClient := network.NewBastionHostsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&BastionHostsClient.Client, o.ResourceManagerAuthorizer)

ConnectionMonitorsClient := network.NewConnectionMonitorsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&ConnectionMonitorsClient.Client, o.ResourceManagerAuthorizer)

Expand Down Expand Up @@ -121,6 +125,7 @@ func BuildClient(o *common.ClientOptions) *Client {
ApplicationGatewaysClient: &ApplicationGatewaysClient,
ApplicationSecurityGroupsClient: &ApplicationSecurityGroupsClient,
AzureFirewallsClient: &AzureFirewallsClient,
BastionHostsClient: &BastionHostsClient,
ConnectionMonitorsClient: &ConnectionMonitorsClient,
DDOSProtectionPlansClient: &DDOSProtectionPlansClient,
ExpressRouteAuthsClient: &ExpressRouteAuthsClient,
Expand Down
1 change: 1 addition & 0 deletions azurerm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ func Provider() terraform.ResourceProvider {
"azurerm_azuread_application": resourceArmActiveDirectoryApplication(),
"azurerm_azuread_service_principal_password": resourceArmActiveDirectoryServicePrincipalPassword(),
"azurerm_azuread_service_principal": resourceArmActiveDirectoryServicePrincipal(),
"azurerm_bastion_host": resourceArmBastionHost(),
"azurerm_batch_account": resourceArmBatchAccount(),
"azurerm_batch_application": resourceArmBatchApplication(),
"azurerm_batch_certificate": resourceArmBatchCertificate(),
Expand Down
279 changes: 279 additions & 0 deletions azurerm/resource_arm_bastion_host.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
package azurerm

import (
"fmt"
"log"
"regexp"

"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceArmBastionHost() *schema.Resource {
return &schema.Resource{
Create: resourceArmBastionHostCreateUpdate,
Read: resourceArmBastionHostRead,
Update: resourceArmBastionHostCreateUpdate,
Delete: resourceArmBastionHostDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateAzureRMBastionHostName,
},
DanielMabbett marked this conversation as resolved.
Show resolved Hide resolved

"location": azure.SchemaLocation(),

"resource_group_name": azure.SchemaResourceGroupName(),

"ip_configuration": {
Type: schema.TypeList,
ForceNew: true,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validateAzureRMBastionIPConfigName,
},
DanielMabbett marked this conversation as resolved.
Show resolved Hide resolved
"subnet_id": {
Type: schema.TypeString,
Required: true,
ValidateFunc: azure.ValidateResourceID,
},
DanielMabbett marked this conversation as resolved.
Show resolved Hide resolved
"public_ip_address_id": {
Type: schema.TypeString,
Required: true,
ValidateFunc: azure.ValidateResourceID,
},
DanielMabbett marked this conversation as resolved.
Show resolved Hide resolved
},
},
},

"dns_name": {
Type: schema.TypeString,
Computed: true,
},

"tags": tags.Schema(),
},
}
}

func resourceArmBastionHostCreateUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).Network.BastionHostsClient
ctx := meta.(*ArmClient).StopContext

log.Println("[INFO] preparing arguments for Azure Bastion Host creation.")

resourceGroup := d.Get("resource_group_name").(string)
name := d.Get("name").(string)
location := azure.NormalizeLocation(d.Get("location").(string))
tags := d.Get("tags").(map[string]interface{})

if requireResourcesToBeImported && d.IsNewResource() {
existing, err := client.Get(ctx, resourceGroup, name)
if err != nil {
if !utils.ResponseWasNotFound(existing.Response) {
return fmt.Errorf("Error checking for presence of existing Bastion Host %q (Resource Group %q): %s", name, resourceGroup, err)
}
}

if existing.ID != nil && *existing.ID != "" {
return tf.ImportAsExistsError("azurerm_bastion_host", *existing.ID)
}
}

parameters := network.BastionHost{
Location: &location,
BastionHostPropertiesFormat: &network.BastionHostPropertiesFormat{
IPConfigurations: expandArmBastionHostIPConfiguration(d.Get("ip_configuration").([]interface{})),
},
Tags: expandTags(tags),
}

future, err := client.CreateOrUpdate(ctx, resourceGroup, name, parameters)
if err != nil {
return fmt.Errorf("Error creating/updating Bastion Host %q (Resource Group %q): %+v", name, resourceGroup, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("Error waiting for creation/update of Bastion Host %q (Resource Group %q): %+v", name, resourceGroup, err)
}

read, err := client.Get(ctx, resourceGroup, name)
if err != nil {
return fmt.Errorf("Error retrieving Bastion Host %q (Resource Group %q): %+v", name, resourceGroup, err)
}

d.SetId(*read.ID)

return resourceArmBastionHostRead(d, meta)
}

func resourceArmBastionHostRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).Network.BastionHostsClient
ctx := meta.(*ArmClient).StopContext

id, err := azure.ParseAzureResourceID(d.Id())
if err != nil {
return err
}

name := id.Path["bastionHosts"]
resourceGroup := id.ResourceGroup

resp, err := client.Get(ctx, resourceGroup, name)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
d.SetId("")
log.Printf("[DEBUG] Bastion Host %q was not found in Resource Group %q - removing from state!", name, resourceGroup)
return nil
}
return fmt.Errorf("Error reading the state of Bastion Host %q: %+v", name, err)
}

DanielMabbett marked this conversation as resolved.
Show resolved Hide resolved
d.Set("name", resp.Name)
d.Set("resource_group_name", resourceGroup)

if location := resp.Location; location != nil {
d.Set("location", azure.NormalizeLocation(*location))
}

if props := resp.BastionHostPropertiesFormat; props != nil {
d.Set("dns_name", props.DNSName)

if ipConfigs := props.IPConfigurations; ipConfigs != nil {
if err := d.Set("ip_configuration", flattenArmBastionHostIPConfiguration(ipConfigs)); err != nil {
return fmt.Errorf("Error flattening `ip_configuration`: %+v", err)
}
}
}

return tags.FlattenAndSet(d, resp.Tags)
}

func resourceArmBastionHostDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).Network.BastionHostsClient
ctx := meta.(*ArmClient).StopContext

id, err := parseAzureResourceID(d.Id())
if err != nil {
return err
}

name := id.Path["bastionHosts"]
resourceGroup := id.ResourceGroup

future, err := client.Delete(ctx, resourceGroup, name)
if err != nil {
return fmt.Errorf("Error deleting Bastion Host %q (Resource Group %q): %+v", name, resourceGroup, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
if !response.WasNotFound(future.Response()) {
return fmt.Errorf("Error waiting for deletion of Bastion Host %q (Resource Group %q): %+v", name, resourceGroup, err)
}
}

return nil
}

func validateAzureRMBastionHostName(v interface{}, k string) (warnings []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf("lowercase letters, highercase letters numbers only are allowed in %q: %q", k, value))
}

if 1 > len(value) {
errors = append(errors, fmt.Errorf("%q cannot be less than 1 characters: %q", k, value))
}

if len(value) > 64 {
errors = append(errors, fmt.Errorf("%q cannot be longer than 64 characters: %q %d", k, value, len(value)))
}

return warnings, errors
}

func validateAzureRMBastionIPConfigName(v interface{}, k string) (warnings []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf("lowercase letters, highercase letters numbers only are allowed in %q: %q", k, value))
}

if 1 > len(value) {
errors = append(errors, fmt.Errorf("%q cannot be less than 1 characters: %q", k, value))
}

if len(value) > 32 {
errors = append(errors, fmt.Errorf("%q cannot be longer than 32 characters: %q %d", k, value, len(value)))
}

return warnings, errors
}

func expandArmBastionHostIPConfiguration(input []interface{}) (ipConfigs *[]network.BastionHostIPConfiguration) {
if len(input) == 0 {
return nil
}

property := input[0].(map[string]interface{})
ipConfName := property["name"].(string)
subID := property["subnet_id"].(string)
pipID := property["public_ip_address_id"].(string)

return &[]network.BastionHostIPConfiguration{
{
Name: &ipConfName,
BastionHostIPConfigurationPropertiesFormat: &network.BastionHostIPConfigurationPropertiesFormat{
Subnet: &network.SubResource{
ID: &subID,
},
PublicIPAddress: &network.SubResource{
ID: &pipID,
},
},
},
}
}

func flattenArmBastionHostIPConfiguration(ipConfigs *[]network.BastionHostIPConfiguration) []interface{} {
result := make([]interface{}, 0)
if ipConfigs == nil {
return result
}

for _, config := range *ipConfigs {
ipConfig := make(map[string]interface{})

if config.Name != nil {
ipConfig["name"] = *config.Name
}

if props := config.BastionHostIPConfigurationPropertiesFormat; props != nil {
if subnet := props.Subnet; subnet != nil {
ipConfig["subnet_id"] = *subnet.ID
}

if pip := props.PublicIPAddress; pip != nil {
ipConfig["public_ip_address_id"] = *pip.ID
}
}

result = append(result, ipConfig)
}
return result
}
Loading