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

Adding Operational Insight Workspace (a.k.a Log Analytics) #331

Merged
merged 28 commits into from
Sep 26, 2017
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
78cca50
Adding operationalinsight workspace
TsuyoshiUshio Sep 13, 2017
db9621c
Set SKU as default
TsuyoshiUshio Sep 13, 2017
357bc06
Follow the naming Rule with using location Sceme
TsuyoshiUshio Sep 22, 2017
ae309e0
Merge branch 'master' into functions-provider
TsuyoshiUshio Sep 22, 2017
d5ebef8
Use resourceGroupNameSchema()
TsuyoshiUshio Sep 23, 2017
75dc27c
Remove comment. I'll write the comment to the documentation.
TsuyoshiUshio Sep 23, 2017
1fe74ea
Adding SKU types validation
TsuyoshiUshio Sep 23, 2017
e86a8a2
Refactor: Remove getSku(). We don't need to write this, we can use Sk…
TsuyoshiUshio Sep 23, 2017
c3408b2
Follow the coding style.
TsuyoshiUshio Sep 23, 2017
2f74b8e
Remove unnecessary comment
TsuyoshiUshio Sep 23, 2017
4d4678f
Error message improvement to dump more for error struct.
TsuyoshiUshio Sep 23, 2017
011e38f
Make error more dump for error struct
TsuyoshiUshio Sep 23, 2017
c821a98
Using utility method.
TsuyoshiUshio Sep 23, 2017
577502d
Adding workspace name validation
TsuyoshiUshio Sep 23, 2017
3e8c7b5
Follow the naming rule
TsuyoshiUshio Sep 23, 2017
a578ba2
Adjust SDK version for operational insight
TsuyoshiUshio Sep 23, 2017
0cd140a
Change provider name and fix the Acceptance testing error
TsuyoshiUshio Sep 24, 2017
ea0692e
Adding the log anaylytics documentation
TsuyoshiUshio Sep 24, 2017
87b6e9a
Fix Travis-CI issue. It prevent make vet works.
TsuyoshiUshio Sep 24, 2017
9b64611
Edit the message for fit the error root cause
TsuyoshiUshio Sep 24, 2017
7c6ffaa
Changing name into OperationalInsight to LogAnalytics
TsuyoshiUshio Sep 25, 2017
a260e4f
Change the validation function to validation library
TsuyoshiUshio Sep 25, 2017
204c2fb
Fix in case of SKU is null
TsuyoshiUshio Sep 25, 2017
68bec07
Fix tyop and Fix the Operational Insight in Log messages
TsuyoshiUshio Sep 25, 2017
01fa92d
Fix Documentation hightlight.
TsuyoshiUshio Sep 25, 2017
5ced761
Comparing the Resource Group name in a case-insensitive manor
tombuildsstuff Sep 26, 2017
8e33ee6
Minor documentation cleanup
tombuildsstuff Sep 26, 2017
57b177f
`azurerm_log_analytics` -> `azurerm_log_analytics_workspace`
tombuildsstuff Sep 26, 2017
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
9 changes: 9 additions & 0 deletions azurerm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/Azure/azure-sdk-for-go/arm/graphrbac"
"github.com/Azure/azure-sdk-for-go/arm/keyvault"
"github.com/Azure/azure-sdk-for-go/arm/network"
"github.com/Azure/azure-sdk-for-go/arm/operationalinsights"
"github.com/Azure/azure-sdk-for-go/arm/postgresql"
"github.com/Azure/azure-sdk-for-go/arm/redis"
"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
Expand Down Expand Up @@ -98,6 +99,8 @@ type ArmClient struct {
eventHubConsumerGroupClient eventhub.ConsumerGroupsClient
eventHubNamespacesClient eventhub.NamespacesClient

workspacesClient operationalinsights.WorkspacesClient

postgresqlConfigurationsClient postgresql.ConfigurationsClient
postgresqlDatabasesClient postgresql.DatabasesClient
postgresqlFirewallRulesClient postgresql.FirewallRulesClient
Expand Down Expand Up @@ -400,6 +403,12 @@ func (c *Config) getArmClient() (*ArmClient, error) {
lgc.Sender = sender
client.localNetConnClient = lgc

opwc := operationalinsights.NewWorkspacesClient(c.SubscriptionID)
setUserAgent(&opwc.Client)
opwc.Authorizer = auth
opwc.Sender = autorest.CreateSender(withRequestLogging())
client.workspacesClient = opwc

pipc := network.NewPublicIPAddressesClientWithBaseURI(endpoint, c.SubscriptionID)
setUserAgent(&pipc.Client)
pipc.Authorizer = auth
Expand Down
56 changes: 56 additions & 0 deletions azurerm/import_arm_operational_insight_workspace_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package azurerm
Copy link
Contributor

Choose a reason for hiding this comment

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

given the resource has been renamed - can we rename this file to import_arm_log_analytics_workspace_test.go?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!


import (
"testing"

"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)

func TestAccAzureRMOperationalInsightWorkspace_importRequiredOnly(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

can we rename this to LogAnalytics?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

resourceName := "azurerm_log_analytics.test"

ri := acctest.RandInt()
config := testAccAzureRMOperationalInsightWorkspace_requiredOnly(ri, testLocation())

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMOperationalInsightWorkspaceDestroy,
Steps: []resource.TestStep{
{
Config: config,
},

{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAzureRMOperationalInsightWorkspace_importRetentionInDaysComplete(t *testing.T) {
resourceName := "azurerm_log_analytics.test"

ri := acctest.RandInt()
config := testAccAzureRMOperationalInsightWorkspace_retentionInDaysComplete(ri, testLocation())

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMOperationalInsightWorkspaceDestroy,
Steps: []resource.TestStep{
{
Config: config,
},

{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
41 changes: 22 additions & 19 deletions azurerm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ func Provider() terraform.ResourceProvider {
"azurerm_network_interface": resourceArmNetworkInterface(),
"azurerm_network_security_group": resourceArmNetworkSecurityGroup(),
"azurerm_network_security_rule": resourceArmNetworkSecurityRule(),
"azurerm_log_analytics": resourceArmOperationalInsightWorkspaceService(),
"azurerm_postgresql_configuration": resourceArmPostgreSQLConfiguration(),
"azurerm_postgresql_database": resourceArmPostgreSQLDatabase(),
"azurerm_postgresql_firewall_rule": resourceArmPostgreSQLFirewallRule(),
Expand Down Expand Up @@ -381,25 +382,26 @@ var providerRegistrationOnce sync.Once

func determineAzureResourceProvidersToRegister(providerList []resources.Provider) map[string]struct{} {
providers := map[string]struct{}{
"Microsoft.Automation": {},
"Microsoft.Cache": {},
"Microsoft.Cdn": {},
"Microsoft.Compute": {},
"Microsoft.ContainerInstance": {},
"Microsoft.ContainerRegistry": {},
"Microsoft.ContainerService": {},
"Microsoft.DBforPostgreSQL": {},
"Microsoft.DocumentDB": {},
"Microsoft.EventGrid": {},
"Microsoft.EventHub": {},
"Microsoft.KeyVault": {},
"microsoft.insights": {},
"Microsoft.Network": {},
"Microsoft.Resources": {},
"Microsoft.Search": {},
"Microsoft.ServiceBus": {},
"Microsoft.Sql": {},
"Microsoft.Storage": {},
"Microsoft.Automation": {},
"Microsoft.Cache": {},
"Microsoft.Cdn": {},
"Microsoft.Compute": {},
"Microsoft.ContainerInstance": {},
"Microsoft.ContainerRegistry": {},
"Microsoft.ContainerService": {},
"Microsoft.DBforPostgreSQL": {},
"Microsoft.DocumentDB": {},
"Microsoft.EventGrid": {},
"Microsoft.EventHub": {},
"Microsoft.KeyVault": {},
"microsoft.insights": {},
"Microsoft.Network": {},
"Microsoft.OperationalInsights": {},
"Microsoft.Resources": {},
"Microsoft.Search": {},
"Microsoft.ServiceBus": {},
"Microsoft.Sql": {},
"Microsoft.Storage": {},
}

// filter out any providers already registered
Expand All @@ -424,6 +426,7 @@ func determineAzureResourceProvidersToRegister(providerList []resources.Provider
func registerAzureResourceProvidersWithSubscription(providerList []resources.Provider, client resources.ProvidersClient) error {
var err error
providerRegistrationOnce.Do(func() {

providers := determineAzureResourceProvidersToRegister(providerList)

var wg sync.WaitGroup
Expand Down
207 changes: 207 additions & 0 deletions azurerm/resource_arm_operational_insight_workspace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package azurerm
Copy link
Contributor

Choose a reason for hiding this comment

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

(as above) - could we rename this to resource_arm_log_analysis_workspace.go

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

resource_arm_log_analytics_workspace.go instead. :) Done!


import (
"fmt"
"log"
"regexp"

"github.com/Azure/azure-sdk-for-go/arm/operationalinsights"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"

"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceArmOperationalInsightWorkspaceService() *schema.Resource {
return &schema.Resource{
Create: resourceArmOperationalInsightWorkspaceCreateUpdate,
Read: resourceArmOperationalInsightWorkspaceRead,
Update: resourceArmOperationalInsightWorkspaceCreateUpdate,
Delete: resourceArmOperationalInsightWorkspaceDelete,
Copy link
Contributor

Choose a reason for hiding this comment

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

as above - could we rename this to LogAnalytics?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateAzureRmOperationalInsightWorkspaceName,
},
"location": locationSchema(),
"resource_group_name": resourceGroupNameSchema(),
"workspace_id": {
Type: schema.TypeString,
Computed: true,
},
"portal_url": {
Type: schema.TypeString,
Computed: true,
},
"sku": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

can we add some validation to this for the possible SKU types, i.e.

ValidateFunc: validation.StringInSlice([]string{
  string(operationalinsights.Free),
  string(operationalinsights.PerNode),
  string(operationalinsights.Premium),
  string(operationalinsights.Standalone),
  string(operationalinsights.Standard),
  string(operationalinsights.Unlimited),
}, true),
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,

Copy link
Contributor

Choose a reason for hiding this comment

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

The DiffSuppressFunc included above should fix this test failure:

sku:                  "free" => "Free" (forces new resource)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh! This is good one! I've done!

ValidateFunc: validation.StringInSlice([]string{
string(operationalinsights.Free),
string(operationalinsights.PerNode),
string(operationalinsights.Premium),
string(operationalinsights.Standalone),
string(operationalinsights.Standard),
string(operationalinsights.Unlimited),
}, true),
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
},
"retention_in_days": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ValidateFunc: validateAzureRmOperationalInsightWorkspaceRetentionInDays,
Copy link
Contributor

Choose a reason for hiding this comment

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

we should be able to swap this out for:

ValidateFunc: validation.IntBetween(30, 730)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

},
"primary_shared_key": {
Type: schema.TypeString,
Computed: true,
},
"secondary_shared_key": {
Type: schema.TypeString,
Computed: true,
},
"tags": tagsSchema(),
},
}
}

func resourceArmOperationalInsightWorkspaceCreateUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).workspacesClient
log.Printf("[INFO] preparing arguments for AzureRM Operational Insight workspace creation.")

name := d.Get("name").(string)
location := d.Get("location").(string)
resGroup := d.Get("resource_group_name").(string)

skuName := d.Get("sku").(string)
sku := &operationalinsights.Sku{
Name: operationalinsights.SkuNameEnum(skuName),
}

retentionInDays := int32(d.Get("retention_in_days").(int))

tags := d.Get("tags").(map[string]interface{})

parameters := operationalinsights.Workspace{
Name: &name,
Location: &location,
Tags: expandTags(tags),
WorkspaceProperties: &operationalinsights.WorkspaceProperties{
Sku: sku,
RetentionInDays: &retentionInDays,
},
}

_, error := client.CreateOrUpdate(resGroup, name, parameters, make(chan struct{}))
err := <-error
if err != nil {
return err
}

read, err := client.Get(resGroup, name)
if err != nil {
return err
}

if read.ID == nil {
return fmt.Errorf("Cannot read Operational Inight Workspace '%s' (resource group %s) ID", name, resGroup)
}

d.SetId(*read.ID)

return resourceArmOperationalInsightWorkspaceRead(d, meta)

}

func resourceArmOperationalInsightWorkspaceRead(d *schema.ResourceData, meta interface{}) error {

client := meta.(*ArmClient).workspacesClient
id, err := parseAzureResourceID(d.Id())
if err != nil {
return err
}
resGroup := id.ResourceGroup
name := id.Path["workspaces"]

resp, err := client.Get(resGroup, name)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
d.SetId("")
return nil
}
return fmt.Errorf("Error making Read request on AzureRM Operational Insight workspaces '%s': %+v", name, err)
}

d.Set("name", resp.Name)
d.Set("location", resp.Location)
d.Set("resource_group_name", resGroup)
d.Set("workspace_id", resp.CustomerID)
d.Set("portal_url", resp.PortalURL)
d.Set("sku", resp.Sku.Name)
Copy link
Contributor

Choose a reason for hiding this comment

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

could we add a check to ensure that the returned SKU object isn't nil here? (this can happen when the Swagger doesn't match the API response, when the SDK is upgraded)

if sku := resp.Sku; sku != nil {
  d.Set("sku", sku.Name)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I didn't know that! Good info!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

d.Set("retention_in_days", resp.RetentionInDays)

sharedKeys, err := client.GetSharedKeys(resGroup, name)
if err != nil {
log.Printf("[ERROR] Unable to List Shared keys for Operatinal Insight workspaces %s: %+v", name, err)
Copy link
Contributor

Choose a reason for hiding this comment

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

minor / not a blocker Operatinal -> Operational

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I remove Operational Insight -> Log Analytics :)

} else {
d.Set("primary_shared_key", sharedKeys.PrimarySharedKey)
d.Set("secondary_shared_key", sharedKeys.SecondarySharedKey)
}

flattenAndSetTags(d, resp.Tags)
return nil
}

func resourceArmOperationalInsightWorkspaceDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).workspacesClient

id, err := parseAzureResourceID(d.Id())
if err != nil {
return err
}
resGroup := id.ResourceGroup
name := id.Path["workspaces"]

resp, err := client.Delete(resGroup, name)

if err != nil {
if utils.ResponseWasNotFound(resp) {
return nil
}

return fmt.Errorf("Error issuing AzureRM delete request for Operational Insight Workspaces '%s': %+v", name, err)
}

return nil
}

func validateAzureRmOperationalInsightWorkspaceName(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)

r, _ := regexp.Compile("^[A-Za-z0-9][A-Za-z0-9-]+[A-Za-z0-9]$")
if !r.MatchString(value) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I can't see a max length listed in the API Documentation - is there one in the Portal we should add checks for?

Copy link
Contributor

@piotrgo piotrgo Sep 21, 2017

Choose a reason for hiding this comment

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

Name requirements are shown in the azure portal as a tooltip.

Workspace name should include 4-63 letters, digits or '-'. The '-' shouldn't be the first or the last symbol.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you @piotrgo ! I added the validation with test.

errors = append(errors, fmt.Errorf("Workspace Name can only contain alphabet, number, and '-' charactor. You can not use '-' as the start and end of the name"))
Copy link
Contributor

Choose a reason for hiding this comment

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

minor and not a blocker charactor -> character

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! Done

}

length := len(value)
if length > 63 || 4 > length {
errors = append(errors, fmt.Errorf("Workspace Name can only be between 4 and 63 letters"))
}

return
}

func validateAzureRmOperationalInsightWorkspaceRetentionInDays(v interface{}, k string) (ws []string, errors []error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

(as above) I think we can remove this function and replace it with the built-in validation.IntBetween(X, Y) method :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed!

value := v.(int)
if value < 30 || value > 730 {
errors = append(errors, fmt.Errorf("The `retention_in_days` can only be between 30 and 730"))
}
return
}
Loading