Skip to content

Commit

Permalink
Adding Operational Insight Workspace (a.k.a Log Analytics) (#331)
Browse files Browse the repository at this point in the history
* Adding operationalinsight workspace

* Set SKU as default

* Follow the naming Rule with using location Sceme

* Use resourceGroupNameSchema()

* Remove comment. I'll write the comment to the documentation.

* Adding SKU types validation

* Refactor: Remove getSku(). We don't need to write this, we can use SkuEnum instead.

* Follow the coding style.

* Remove unnecessary comment

* Error message improvement to dump more for error struct.

* Make error more dump for error struct

* Using utility method.

* Adding workspace name validation

* Follow the naming rule

* Adjust SDK version for operational insight

* Change provider name and fix the Acceptance testing error

Talked with the Log Analytics production team, they'd love to go log analytics. I change the name. However, the REST-API name is operational analytics. I change the schema name as log analytics however, keep the source code as operational insights for the consistency of the Azure SDK naming.

Also, I fix the Acceptance testing error. The root cause of the problem is the Retention in days. It is not stated however, we can use 30 - 730. I add validation function and test for that. https://blogs.msdn.microsoft.com/canberrapfe/2017/01/25/change-oms-log-analytics-retention-period-in-the-azure-portal/ The blog says 31 - 730, however 30 is default value it works.

* Adding the log anaylytics documentation

* Fix Travis-CI issue. It prevent make vet works.

* Edit the message for fit the error root cause

* Changing name into OperationalInsight to LogAnalytics

* Change the validation function to validation library

* Fix in case of SKU is null

* Fix tyop and Fix the Operational Insight in Log messages

* Fix Documentation hightlight.

* Comparing the Resource Group name in a case-insensitive manor

* Minor documentation cleanup

* `azurerm_log_analytics` -> `azurerm_log_analytics_workspace`
  • Loading branch information
TsuyoshiUshio authored and tombuildsstuff committed Sep 26, 2017
1 parent 2506354 commit 9a3a748
Show file tree
Hide file tree
Showing 15 changed files with 2,510 additions and 19 deletions.
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_log_analytics_workspace_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package azurerm

import (
"testing"

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

func TestAccAzureRMLogAnalyticsWorkspace_importRequiredOnly(t *testing.T) {
resourceName := "azurerm_log_analytics_workspace.test"

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

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

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

func TestAccAzureRMLogAnalyticsWorkspace_importRetentionInDaysComplete(t *testing.T) {
resourceName := "azurerm_log_analytics_workspace.test"

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

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMLogAnalyticsWorkspaceDestroy,
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 @@ -109,6 +109,7 @@ func Provider() terraform.ResourceProvider {
"azurerm_lb_probe": resourceArmLoadBalancerProbe(),
"azurerm_lb_rule": resourceArmLoadBalancerRule(),
"azurerm_local_network_gateway": resourceArmLocalNetworkGateway(),
"azurerm_log_analytics_workspace": resourceArmLogAnalyticsWorkspace(),
"azurerm_managed_disk": resourceArmManagedDisk(),
"azurerm_network_interface": resourceArmNetworkInterface(),
"azurerm_network_security_group": resourceArmNetworkSecurityGroup(),
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
209 changes: 209 additions & 0 deletions azurerm/resource_arm_log_analytics_workspace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
package azurerm

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 resourceArmLogAnalyticsWorkspace() *schema.Resource {
return &schema.Resource{
Create: resourceArmLogAnalyticsWorkspaceCreateUpdate,
Read: resourceArmLogAnalyticsWorkspaceRead,
Update: resourceArmLogAnalyticsWorkspaceCreateUpdate,
Delete: resourceArmLogAnalyticsWorkspaceDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateAzureRmLogAnalyticsWorkspaceName,
},

"location": locationSchema(),

"resource_group_name": resourceGroupNameDiffSuppressSchema(),

"sku": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
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: validation.IntBetween(30, 730),
},

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

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

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

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

"tags": tagsSchema(),
},
}
}

func resourceArmLogAnalyticsWorkspaceCreateUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).workspacesClient
log.Printf("[INFO] preparing arguments for AzureRM Log Analytics 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 Log Analytics Workspace '%s' (resource group %s) ID", name, resGroup)
}

d.SetId(*read.ID)

return resourceArmLogAnalyticsWorkspaceRead(d, meta)

}

func resourceArmLogAnalyticsWorkspaceRead(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 Log Analytics 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)
if sku := resp.Sku; sku != nil {
d.Set("sku", sku.Name)
}
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 Log Analytics workspaces %s: %+v", name, err)
} else {
d.Set("primary_shared_key", sharedKeys.PrimarySharedKey)
d.Set("secondary_shared_key", sharedKeys.SecondarySharedKey)
}

flattenAndSetTags(d, resp.Tags)
return nil
}

func resourceArmLogAnalyticsWorkspaceDelete(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 Log Analytics Workspaces '%s': %+v", name, err)
}

return nil
}

func validateAzureRmLogAnalyticsWorkspaceName(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) {
errors = append(errors, fmt.Errorf("Workspace Name can only contain alphabet, number, and '-' character. You can not use '-' as the start and end of the name"))
}

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

return
}
Loading

0 comments on commit 9a3a748

Please sign in to comment.