Enable-AzureRMDiagnosticsEventHubs.ps1

<#PSScriptInfo
 
.VERSION 1.0
 
.GUID e54c83df-440c-4933-8be8-30d0ff7f966c
 
.AUTHOR jbritt@microsoft.com
 
.COMPANYNAME Microsoft
 
.COPYRIGHT Microsoft
 
.TAGS
 
.LICENSEURI
 
.PROJECTURI
http://aka.ms/azmononboarding
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
  July 16, 2018
  Intial Release
#>


<#
.SYNOPSIS
  Configure a resource (given a resource ID in Azure) to enable Azure Diagnostics and send that data to an Event Hub.
  This EHID specified as a parameter is the EventHubAuthorizationRuleId of the Event Hub within an Azure Subscription in the following format
   
  "/subscriptions/<subscriptionID GUID>/resourceGroups/<EH Resource Group>/providers/Microsoft.EventHub/namespaces/<Event Hub Name>/AuthorizationRules/RootManageSharedAccessKey"
 
  Note This script currently supports onboarding Azure resources that support Azure Diagnostics (metrics and logs) to Event Hubs only.
   
  Use of Update switch "-Update $True" updates the resource to a new event hub endpoint and enable diagnostics
  or just refresh the configuration for all resources.
 
  Use of "-Force" provides the ability to launch this script without prompting, if all required parameters are provided.
   
.DESCRIPTION
  This script takes a SubscriptionID, ResourceType, ResourceGroup and an EventHubAuthorizationRuleId as parameters, analyzes the subscription or
  specific ResourceGroup defined for the resources specified in $Resources, and enables those resources for diagnostic metrics and logs
  also enabling the EventHubAuthorizationRuleId for the Event Hub endpoint to receive these metrics and logs.
 
.PARAMETER Update
    Specify update if you want to update all resources regardless of configuration
 
.PARAMETER EHID
    The EventHubAuthorizationRuleId of your Event Hub within Azure
     
.PARAMETER SubscriptionId
    The subscriptionID of the Azure Subscription that contains the resources you want to update/configure
 
.PARAMETER ResourceType
    The ResourceType you want to update within your Azure Subscription
     
.PARAMETER ResourceGroupName
    If desired, use a resourcegroup instead of updating all resources of a particular type within an Azure subscription
 
.PARAMETER ResourceName
    If desired, use a resource name instead of updating all resources of a particular type within an Azure subscription
 
.PARAMETER Force
    Use Force to run silently [providing all parameters needed for silent mode - see get-help <scriptfile> -examples]
 
.PARAMETER DisableLogsMetrics
    Use DisableLogsMetrics to remove logs and metrics for a resource
 
.EXAMPLE
  .\Enable-AzureRMDiagnosticsEventHubs.ps1 -EHID "/subscriptions/fd2323a9-2324-4d2a-90f6-7e6c2fe03512/resourceGroups/EH-EAST-USE/providers/Microsoft.EventHub/namespaces/EH001/AuthorizationRules/RootManageSharedAccessKey" -SubscriptionId "fd2323a9-2324-4d2a-90f6-7e6c2fe03512" -ResourceType "Microsoft.Sql/servers/databases" -ResourceGroup "RGName"
  Take in parameters and prompt for confirmation before continuing.
 
.EXAMPLE
  .\Enable-AzureRMDiagnosticsEventHubs.ps1 -EHID "/subscriptions/fd2323a9-2324-4d2a-90f6-7e6c2fe03512/resourceGroups/EH-EAST-USE/providers/Microsoft.EventHub/namespaces/EH001/AuthorizationRules/RootManageSharedAccessKey" -SubscriptionId "fd2323a9-2324-4d2a-90f6-7e6c2fe03512" -ResourceType "Microsoft.Sql/servers/databases" -ResourceGroup "RGName" -Force
  Take in parameters and execute silently without prompting using Force.
 
.EXAMPLE
  .\Enable-AzureRMDiagnosticsEventHubs.ps1 -EHID "/subscriptions/fd2323a9-2324-4d2a-90f6-7e6c2fe03512/resourceGroups/EH-EAST-USE/providers/Microsoft.EventHub/namespaces/EH001/AuthorizationRules/RootManageSharedAccessKey" -SubscriptionId "fd2323a9-2324-4d2a-90f6-7e6c2fe03512" -ResourceType "Microsoft.Sql/servers/databases" -ResourceGroup "RGName" -Force -Update
  Take in parameters and execute silently without prompting and update all resources with a new EHID
 
  NOTE: Remove -Force to be prompted.
 
.EXAMPLE
  .\Enable-AzureRMDiagnosticsEventHubs.ps1 -Verbose -Debug
  To Support Verbose and Debug log Output
 
.EXAMPLE
  .\Enable-AzureRMDiagnosticsEventHubs.ps1 -EHID "/subscriptions/fd2323a9-2324-4d2a-90f6-7e6c2fe03512/resourceGroups/EH-EAST-USE/providers/Microsoft.EventHub/namespaces/EH001/AuthorizationRules/RootManageSharedAccessKey" -SubscriptionId "fd2323a9-2324-4d2a-90f6-7e6c2fe03512" -ResourceType "Microsoft.Sql/servers/databases" -ResourceGroup "RGName" -Force -DisableLogsMetrics
  Take in parameters and execute silently without prompting and disable Logs and Metrics
  for a specific set of resources w/in a resource group and resourceType defined.
 
  NOTE: EHID can be any string in this instance since we are disabling.
 
.NOTES
   AUTHOR: Jim Britt Senior Program Manager - Azure CAT
   LASTEDIT: July 16, 2018
 
   July 16, 2018
   Intial Release
 
.LINK
    This script posted to and discussed at the following locations:
        https://www.powershellgallery.com/packages/Enable-AzureRMDiagnosticsEventHubs
        http://aka.ms/azmononboarding
#>

param
(
    # Use Update to refresh a configuration such as changing to a new Event Hub target endpoint
    [switch]$Update,

    # Provide EventHubAuthoriziationRuleId of and Event Hub within same Azure AD Tenant
    # to send multiple subs to one Event Hub endpoint
    [Parameter(Mandatory=$False,ParameterSetName='default')]
    [Parameter(Mandatory=$True,ParameterSetName='force')]
    [string]$EHID,

    # Provide SubscriptionID to bypass subscription listing
    [Parameter(Mandatory=$False,ParameterSetName='default')]
    [Parameter(Mandatory=$True,ParameterSetName='force')]
    [guid]$SubscriptionId,

    # Add ResourceType to reduce scope to Resource Type instead of entire list of resources to scan
    [Parameter(Mandatory=$False,ParameterSetName='default')]
    [Parameter(Mandatory=$True,ParameterSetName='force')]
    [string]$ResourceType,

    # Add a ResourceGroup name to reduce scope from entire Azure Subscription to RG
    [string]$ResourceGroupName,

    # Add a ResourceName name to reduce scope from entire Azure Subscription to specific named resource
    [string]$ResourceName,

    # Use Force to run in silent mode (requires certain parameters to be provided)
    [Parameter(Mandatory=$True,ParameterSetName='force')]
    [switch]$Force,

    # Use to remove the configuration of metrics for a selected resource type
    [switch]$DisableLogsMetrics

   
)
# FUNCTIONS
# Get the ResourceType listing from all ResourceTypes capable in this subscription
# to be sent to log analytics - use "-ResourceType" param to bypass
function Get-ResourceType (
    [Parameter(Mandatory=$True)]
    [array]$allResources
    )
{
    $analysis = @()
    
    foreach($resource in $allResources)
    {
        $Categories =@();
        $metrics = $false #initialize metrics flag to $false
        $logs = $false #initialize logs flag to $false
    
        if (! $analysis.where({$_.ResourceType -eq $resource.ResourceType}))
        {
            try
            {
                Write-Verbose "Checking $($resource.ResourceType)"
                $setting = Get-AzureRmDiagnosticSetting -ResourceId $resource.ResourceId -ErrorAction Stop
                # If logs are supported or metrics on each resource, set value as $True
                if ($setting.Logs) 
                { 
                    $logs = $true
                    $Categories = $setting.Logs.category 
                }


                if ($setting.Metrics) 
                { 
                    $metrics = $true
                }   
            }
            catch {}
            finally
            {
                $object = New-Object -TypeName PSObject -Property @{'ResourceType' = $resource.ResourceType; 'Metrics' = $metrics; 'Logs' = $logs; 'Categories' = $Categories}
                $analysis += $object
            }
        }
    }
    # Return the list of supported resources
    $analysis
}

# Enable Diagnostics and set EHID for each resource (if not already set)
function Set-Resource
{
    [cmdletbinding()]
    
    param
    (
        [Parameter(Mandatory=$True)]
        [array]$Resources,
        [switch]$Update,
        [string]$EHID,
        [psobject]$DiagnosticCapability,
        [string]$EHRegion
    )
    Write-Host "Processing resources. Please wait...."
    Foreach($Resource in $Resources)
    {
        If(!($DisableLogsMetrics))
        {
            if(!($Resource.location -eq $EHRegion))
            {
                Write-Host "Resource $($Resource.Name) is in $($Resource.location) - different region from your Event Hub Rule ID in $EHRegion"  -ForegroundColor Yellow
                break
            }
            $EHIDOK = $True
            $ResourceDiagnosticSetting = get-AzureRmDiagnosticSetting -ResourceId $Resource.ResourceId
            if($ResourceDiagnosticSetting.EventHubAuthorizationRuleId -ne $null -and $ResourceDiagnosticSetting.EventHubAuthorizationRuleId -ne $EHID -and $Update -eq $False)
            {
                # If update switch not used, EHIDOK is set to false and warning is thrown
                $EHIDOK = $False
                $EH = ($ResourceDiagnosticSetting.EventHubAuthorizationRuleId -split "/namespaces/", 2)[1]
                $EH = ($EH -split "/", 2)[0]
                Write-Host "Resource $($Resource.Name) is already enabled for Event Hub $EH. " -NoNewline
                write-host "Use -Update" -ForegroundColor Yellow

            }
            # Update switch enables updating EventHubRuleID if one is already specified.
            if($Update -eq $True -and $EHIDOK -eq $True)
            {
                try
                {
                    $EH = ($ResourceDiagnosticSetting.EventHubAuthorizationRuleId -split "/namespaces/", 2)[1]
                    $EH = ($EH -split "/", 2)[0]
                    
                    $Diag = Set-AzureRmDiagnosticSetting -EventHubAuthorizationRuleId $EHID -ResourceId $Resource.ResourceId
                    if($Diag){Write-Host "Event Hub for existing resource $($Resource.Name) was updated to $EH."}
                }
                catch
                {
                    write-host "An error occurred setting diagnostics on $($Resource.Name)"
                }
            }
        
            if($DiagnosticCapability.logs -eq $True -OR $DiagnosticCapability.metrics -eq $True)
            {
                try
                {
                    $Diag = Set-AzureRmDiagnosticSetting -EventHubAuthorizationRuleId $EHID -ResourceId $Resource.ResourceId -Enabled $True
                    if($Diag){Write-Host "Resource $($Resource.Name) was enabled for all Logs and Metrics"}
                }
                catch
                {
                    write-host "An error occurred setting diagnostics on $($Resource.Name) for logs"
                }

            }
        }
        # Disable logs and metrics on a resource(s) if logs / metrics are a capablity supported on the resource(s)
        If($DisableLogsMetrics -and ($DiagnosticCapability.logs -eq $True -OR $DiagnosticCapability.Metrics -eq $True))
        {
            $ResourceDiagnosticSetting = get-AzureRmDiagnosticSetting -ResourceId $Resource.ResourceId
            if($ResourceDiagnosticSetting.ServiceBusRuleId -ne $Null -or $ResourceDiagnosticSetting.StorageAccountId -ne $Null -or $ResourceDiagnosticSetting.WorkspaceId -ne $Null)
            {
                try
                {
                    $SetEHRuleIDToNull = Set-AzureRmDiagnosticSetting -ResourceId $Resource.ResourceId -EventHubAuthorizationRuleId $Null
                    Write-Host "Resource $($Resource.Name) disabled for logs / metrics to event hub"
                }
                catch
                {
                    write-host "An error occurred removing diagnostic log / metrics on $($Resource.Name)"
                }

            }
            else
            {
                try
                {
                    $DisableLogsandMetrics = Set-AzureRmDiagnosticSetting -ResourceId $Resource.ResourceId -Enabled $False
                }
                catch
                {
                    write-host "An error occurred removing diagnostic log / metrics on $($Resource.Name)"
                }
            }
        }
    }
}

# Function used to build numbers in selection tables for menus
function Add-IndexNumberToArray 
(
    [Parameter(Mandatory=$True)]
    [array]$array
)

{
    for($i=0; $i -lt $array.Count; $i++) 
    { 
        Add-Member -InputObject $array[$i] -Name "#" -Value ($i+1) -MemberType NoteProperty 
    }
    $array
}

# Need to get region to ensure we are w/in region for configuration.
# EventHubs cannot receive data from other regions
function Get-EventHubRegion
(
    [Parameter(Mandatory=$True)]
    $EHID
)
{
    # Establish number of elements in ResourceID for Event Hub Rule
    $cnt = $($EHID.Split("/").Count+1)
    
    # Enumerate Name
    $EVENTHUBN = $($EHID.Split("/", $cnt)[8]) # Name
    $ResourceType = "Microsoft.EventHub/namespaces"
    
    # Return object Details for EHRuleID
    $ehoBJECT = Find-AzureRmResource -ResourceType $ResourceType -ResourceNameEquals $EVENTHUBN 
    
    # Return Location
    # Note EventHubs can only receive data from the same region (EastUS to EastUS, EastUS 2 to EastUS 2, etc.)
    $($ehoBJECT.Location)
}

# Let's validate this is a properly formatted EventHub rule ID to the best of our ability
function ValidateID
{
    param
    (
        [Parameter(Mandatory=$False)]$EHID

    )
    $ValidID = $False
    $cnt = 0
    
    IF($EHID)
    {
        $cnt = $($EHID.Split("/").Count+1)
        if($($EHID.Split("/", $Cnt)[1]) -eq "Subscriptions" -and 
         $($EHID.Split("/", $Cnt)[6]) -eq "Microsoft.EventHub" -and 
         $cnt -ge 12)
        {
            # Valid EH RuleID
            $ValidID = $True
        }
            
        else
        {
            write-host "Invalid value passed to EHID" -ForegroundColor Red
            $ValidID = $False
        }
    }
    $ValidID
}

# MAIN SCRIPT
# Variable Definitions
[array]$Resources = @()

# Login to Azure - if already logged in, use existing credentials.
Write-Host "Authenticating to Azure..." -ForegroundColor Cyan
try
{
    $AzureLogin = Get-AzureRmSubscription
}
catch
{
    $null = Login-AzureRmAccount
    $AzureLogin = Get-AzureRmSubscription
}

# Authenticate to Azure if not already authenticated
# Ensure this is the subscription where your Azure Resources are you want to send diagnostic data from
If($AzureLogin -and !($SubscriptionID))
{
    [array]$SubscriptionArray = Add-IndexNumberToArray (Get-AzureRmSubscription) 
    [int]$SelectedSub = 0

    # use the current subscription if there is only one subscription available
    if ($SubscriptionArray.Count -eq 1) 
    {
        $SelectedSub = 1
    }
    # Get SubscriptionID if one isn't provided
    while($SelectedSub -gt $SubscriptionArray.Count -or $SelectedSub -lt 1)
    {
        Write-host "Please select a subscription from the list below"
        $SubscriptionArray | select "#", Id, Name | ft
        try
        {
            $SelectedSub = Read-Host "Please enter a selection from 1 to $($SubscriptionArray.count)"
        }
        catch
        {
            Write-Warning -Message 'Invalid option, please try again.'
        }
    }
    if($($SubscriptionArray[$SelectedSub - 1].Name))
    {
        $SubscriptionName = $($SubscriptionArray[$SelectedSub - 1].Name)
    }
    elseif($($SubscriptionArray[$SelectedSub - 1].SubscriptionName))
    {
        $SubscriptionName = $($SubscriptionArray[$SelectedSub - 1].SubscriptionName)
    }
    write-verbose "You Selected Azure Subscription: $SubscriptionName"
    
    if($($SubscriptionArray[$SelectedSub - 1].SubscriptionID))
    {
        [guid]$SubscriptionID = $($SubscriptionArray[$SelectedSub - 1].SubscriptionID)
    }
    if($($SubscriptionArray[$SelectedSub - 1].ID))
    {
        [guid]$SubscriptionID = $($SubscriptionArray[$SelectedSub - 1].ID)
    }
}

if(($EHID) -and !($DisableLogsMetrics))
{
    # Validate Event Hub Rule ID is in proper format - if not exit
    $ValidID = ValidateID -EHID $EHID
    if(!($ValidID)){exit}

    $cnt = $($EHID.Split("/").Count+1)        
    $EHSub = $($EHID.Split("/", $Cnt)[2])

    # Select Sub for EventHub to get region as a requirement
    Write-Host "Selecting Azure Subscription of Event Hub to get region..." -ForegroundColor Cyan
    $Null = Select-AzureRmSubscription -SubscriptionId $EHSub
    # Get Region for EHRuleID to ensure we are in same region as resource
    $EHRegion = Get-EventHubRegion -EHID $EHID
    write-host "Event Hub is in the $EHRegion region..." -ForegroundColor Cyan
}

Write-Host "Selecting Azure Subscription: $($SubscriptionID.Guid) ..." -ForegroundColor Cyan
$Null = Select-AzureRmSubscription -SubscriptionId $SubscriptionID.Guid

# Build a list of Event Hubs to choose from. If an event hub is in another subscription
# provide the EventHubAuthoriziationRuleId of that Event Hub as a parameter
# *** Event Hub currently must be within the same tenant as the resource being configured ***
[array]$eventHubs=@()
if(!($EHID) -and !($DisableLogsMetrics))
{
    try
    {
        $eventHubs = Add-IndexNumberToArray (Get-AzureRmEventHubNamespace) 
        Write-Host "Generating a list of Event Hubs from Azure Subscription Selected..." -ForegroundColor Cyan

        [int]$SelectedEH = 0
        if ($eventHubs.Count -eq 1)
        {
            $SelectedEH = 1
        }

        # Get EventHubAuthoriziationRuleId if one isn't provided
        while($SelectedEH -gt $eventHubs.Count -or $SelectedEH -lt 1 -and $eventHubs -ne $Null)
        {
            Write-Host "Please select an Event Hub from the list below"
            $eventHubs| select "#", Name, Location, ResourceGroup, Id | ft
            if($eventHubs.count -ne 0)
            {

                try
                {
                    $SelectedEH = Read-Host "Please enter a selection from 1 to $($eventHubs.count)"
                }
                catch
                {
                    Write-Warning -Message 'Invalid option, please try again.'
                }
            }
        }
    }
    catch
    {
        Write-Warning -Message 'No Event Hubs found - try specifying parameter EHID'
    }
    If($eventHubs)
    {
        Write-Host "You Selected Event Hub: " -nonewline -ForegroundColor Cyan
        Write-Host "$($eventHubs[$SelectedEH - 1].Name)" -ForegroundColor Yellow
        $EH = $eventHubs[$SelectedEH - 1]
        $EHID = $(Get-AzureRmEventHubAuthorizationRule -ResourceGroupName $EH.ResourceGroup -Namespace $EH.Name).id
    }
    else
    {
        Throw "No OMS Event Hubs available in selected subscription $SubscriptionID"
    }
    # Get Region for EHRuleID to ensure we are in same region as resource
    $EHRegion = Get-EventHubRegion -EHID $EHID
    write-host "Event Hub is in the $EHRegion region..." -ForegroundColor Cyan

}

# Determine which resourcetype to search on
[array]$ResourcesToCheck = @()
[array]$DiagnosticCapable=@()
[array]$Logcategories = @()

# Build parameter set according to parameters provided.
$FindResourceParams = @{}
if($ResourceType)
{
    $FindResourceParams['ResourceType'] = $ResourceType
}
if($ResourceGroupName)
{
    $FindResourceParams['ResourceGroupNameEquals'] = $ResourceGroupName
}
if($ResourceName)
{
    $FindResourceParams['ResourceNameEquals'] = $ResourceName
}
$ResourcesToCheck = Find-AzureRmResource @FindResourceParams 

# If resourceType defined, ensure it can support diagnostics configuration
if($ResourceType)
{
    try
    {
        $Resources = $ResourcesToCheck
        $DiagnosticCapable = Get-ResourceType -allResources $Resources
        [int]$ResourceTypeToProcess = 0
        if ( $DiagnosticCapable.Count -eq 1)
        {
            $ResourceTypeToProcess = 1
        }
    }
    catch
    {
        Throw "No diagnostic capable resources of type $ResourceType available in selected subscription $SubscriptionID"
    }

}

# Gather a list of resources supporting Azure Diagnostic logs and metrics and display a table
if(!($ResourceType))
{
    Write-Host "Gathering a list of monitorable Resource Types from Azure Subscription ID " -NoNewline -ForegroundColor Cyan
    Write-Host "$SubscriptionId..." -ForegroundColor Yellow
    try
    {
        $DiagnosticCapable = Add-IndexNumberToArray (Get-ResourceType $ResourcesToCheck).where({$_.metrics -eq $True -or $_.Logs -eq $True}) 
        [int]$ResourceTypeToProcess = 0
        if ( $DiagnosticCapable.Count -eq 1)
        {
            $ResourceTypeToProcess = 1
        }
        while($ResourceTypeToProcess -gt $DiagnosticCapable.Count -or $ResourceTypeToProcess -lt 1 -and $Force -ne $True)
        {
            Write-Host "The table below are the resource types that support sending diagnostics to Event Hub"
            $DiagnosticCapable | select "#", ResourceType, Metrics, Logs |ft
            try
            {
                $ResourceTypeToProcess = Read-Host "Please select a number from 1 - $($DiagnosticCapable.count) to enable (""True"" = supported configuration)"
            }
            catch
            {
                Write-Warning -Message 'Invalid option, please try again.'
            }
        }
        $ResourceType = $DiagnosticCapable[$ResourceTypeToProcess -1].ResourceType
        # Find all resources for $ResourceType defined
        $Resources = $ResourcesToCheck.where({$_.ResourceType -eq $ResourceType})
    }
    catch
    {
        Throw "No diagnostic capable resources available in selected subscription $SubscriptionID"
    }
}

# Convert string to array
if($CategoriesChosen)
{
    # Trim spaces out
    $CategoriesChosen = $CategoriesChosen.replace(" ","")
    
    # Define our array of log categories
    [array]$Logcategories = ($CategoriesChosen -split ",")
}

# Validate customer wants to continue to update all resources in ResourceType selected
# If Force used, will update without prompting
if ($Force -OR $PSCmdlet.ShouldContinue("This operation will update $($Resources.Count) $ResourceType resources in your subscription. Continue?",$ResourceType) )
{
        Write-Host "Configuring $($Resources.Count) [$ResourceType] resources in your subscription." 
        Set-Resource -Resources $Resources -Update:$Update -EHID $EHID `
            -DiagnosticCapability $DiagnosticCapable[$ResourceTypeToProcess -1] `
            -EHRegion $EHRegion
        Write-Host "Complete" -ForegroundColor Cyan
}
else
{
        Write-Host "You selected No - exiting"
        Write-Host "Complete" -ForegroundColor Cyan
}