ResourceManagerStartup.ps1
# ---------------------------------------------------------------------------------- # # Copyright Microsoft Corporation # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ---------------------------------------------------------------------------------- @{ # Sql aliases "Get-AzureRmSqlDatabaseServerAuditingPolicy" = "Get-AzureRmSqlServerAuditingPolicy"; "Remove-AzureRmSqlDatabaseServerAuditing" = "Remove-AzureRmSqlServerAuditing"; "Set-AzureRmSqlDatabaseServerAuditingPolicy" = "Set-AzureRmSqlServerAuditingPolicy"; "Use-AzureRmSqlDatabaseServerAuditingPolicy" = "Use-AzureRmSqlServerAuditingPolicy"; # Storage aliases "Get-AzureRmStorageContainerAcl" = "Get-AzureRmStorageContainer"; "Start-CopyAzureStorageBlob" = "Start-AzureRmStorageBlobCopy"; "Stop-CopyAzureStorageBlob" = "Stop-AzureRmStorageBlobCopy"; }.GetEnumerator() | Select @{Name='Name'; Expression={$_.Key}}, @{Name='Value'; Expression={$_.Value}} | New-Alias -Description "AzureAlias" # Authorization script commandlet that builds on top of existing Insights comandlets. # This commandlet gets all events for the "Microsoft.Authorization" resource provider by calling the "Get-AzureRmResourceProviderLog" commandlet function Get-AzureRmAuthorizationChangeLog { [CmdletBinding()] param( [parameter(Mandatory=$false, ValueFromPipelineByPropertyName=$true, HelpMessage = "The start time. Optional If both StartTime and EndTime are not provided, defaults to querying for the past 1 hour. Maximum allowed difference in StartTime and EndTime is 15 days")] [DateTime] $StartTime, [parameter(Mandatory=$false, ValueFromPipelineByPropertyName=$true, HelpMessage = "The end time. Optional. If both StartTime and EndTime are not provided, defaults to querying for the past 1 hour. Maximum allowed difference in StartTime and EndTime is 15 days")] [DateTime] $EndTime ) PROCESS { # Get all events for the "Microsoft.Authorization" provider by calling the Insights commandlet $events = Get-AzureRmResourceProviderLog -ResourceProvider "Microsoft.Authorization" -DetailedOutput -StartTime $StartTime -EndTime $EndTime $startEvents = @{} $endEvents = @{} $offlineEvents = @() # StartEvents and EndEvents will contain matching pairs of logs for when role assignments (and definitions) were created or deleted. # i.e. A PUT on roleassignments will have a Start-End event combination and a DELETE on roleassignments will have another Start-End event combination $startEvents = $events | ? { $_.httpRequest -and $_.Status -ieq "Started" } $events | ? { $_.httpRequest -and $_.Status -ne "Started" } | % { $endEvents[$_.OperationId] = $_ } # This filters non-RBAC events like classic administrator write or delete $events | ? { $_.httpRequest -eq $null } | % { $offlineEvents += $_ } $output = @() # Get all role definitions once from the service and cache to use for all 'startevents' $azureRoleDefinitionCache = @{} Get-AzureRmRoleDefinition | % { $azureRoleDefinitionCache[$_.Id] = $_ } $principalDetailsCache = @{} # Process StartEvents # Find matching EndEvents that succeeded and relating to role assignments only $startEvents | ? { $endEvents.ContainsKey($_.OperationId) ` -and $endEvents[$_.OperationId] -ne $null ` -and $endevents[$_.OperationId].OperationName.StartsWith("Microsoft.Authorization/roleAssignments", [System.StringComparison]::OrdinalIgnoreCase) ` -and $endEvents[$_.OperationId].Status -ieq "Succeeded"} | % { $endEvent = $endEvents[$_.OperationId]; # Create the output structure $out = "" | select Timestamp, Caller, Action, PrincipalId, PrincipalName, PrincipalType, Scope, ScopeName, ScopeType, RoleDefinitionId, RoleName $out.Timestamp = Get-Date -Date $endEvent.EventTimestamp -Format u $out.Caller = $_.Caller if ($_.HttpRequest.Method -ieq "PUT") { $out.Action = "Granted" if ($_.Properties.Content.ContainsKey("requestbody")) { $messageBody = ConvertFrom-Json $_.Properties.Content["requestbody"] } $out.Scope = $_.Authorization.Scope } elseif ($_.HttpRequest.Method -ieq "DELETE") { $out.Action = "Revoked" if ($endEvent.Properties.Content.ContainsKey("responseBody")) { $messageBody = ConvertFrom-Json $endEvent.Properties.Content["responseBody"] } } if ($messageBody) { # Process principal details $out.PrincipalId = $messageBody.properties.principalId if ($out.PrincipalId -ne $null) { # Get principal details by querying Graph. Cache principal details and read from cache if present $principalId = $out.PrincipalId if($principalDetailsCache.ContainsKey($principalId)) { # Found in cache $principalDetails = $principalDetailsCache[$principalId] } else { # not in cache $principalDetails = "" | select Name, Type $user = Get-AzureRmADUser -ObjectId $principalId if ($user) { $principalDetails.Name = $user.DisplayName $principalDetails.Type = "User" } else { $group = Get-AzureRmADGroup -ObjectId $principalId if ($group) { $principalDetails.Name = $group.DisplayName $principalDetails.Type = "Group" } else { $servicePrincipal = Get-AzureRmADServicePrincipal -objectId $principalId if ($servicePrincipal) { $principalDetails.Name = $servicePrincipal.DisplayName $principalDetails.Type = "Service Principal" } } } # add principal details to cache $principalDetailsCache.Add($principalId, $principalDetails); } $out.PrincipalName = $principalDetails.Name $out.PrincipalType = $principalDetails.Type } # Process scope details if ([string]::IsNullOrEmpty($out.Scope)) { $out.Scope = $messageBody.properties.Scope } if ($out.Scope -ne $null) { # Remove the authorization provider details from the scope, if present if ($out.Scope.ToLower().Contains("/providers/microsoft.authorization")) { $index = $out.Scope.ToLower().IndexOf("/providers/microsoft.authorization") $out.Scope = $out.Scope.Substring(0, $index) } $scope = $out.Scope $resourceDetails = "" | select Name, Type $scopeParts = $scope.Split('/', [System.StringSplitOptions]::RemoveEmptyEntries) $len = $scopeParts.Length if ($len -gt 0 -and $len -le 2 -and $scope.ToLower().Contains("subscriptions")) { $resourceDetails.Type = "Subscription" $resourceDetails.Name = $scopeParts[1] } elseif ($len -gt 0 -and $len -le 4 -and $scope.ToLower().Contains("resourcegroups")) { $resourceDetails.Type = "Resource Group" $resourceDetails.Name = $scopeParts[3] } elseif ($len -ge 6 -and $scope.ToLower().Contains("providers")) { $resourceDetails.Type = "Resource" $resourceDetails.Name = $scopeParts[$len -1] } $out.ScopeName = $resourceDetails.Name $out.ScopeType = $resourceDetails.Type } # Process Role definition details $out.RoleDefinitionId = $messageBody.properties.roleDefinitionId if ($out.RoleDefinitionId -ne $null) { if ($azureRoleDefinitionCache[$out.RoleDefinitionId]) { $out.RoleName = $azureRoleDefinitionCache[$out.RoleDefinitionId].Name } else { $out.RoleName = "" } } } $output += $out } # start event processing complete # Filter classic admins events $offlineEvents | % { if($_.Status -ne $null -and $_.Status -ieq "Succeeded" -and $_.OperationName -ne $null -and $_.operationName.StartsWith("Microsoft.Authorization/ClassicAdministrators", [System.StringComparison]::OrdinalIgnoreCase)) { $out = "" | select Timestamp, Caller, Action, PrincipalId, PrincipalName, PrincipalType, Scope, ScopeName, ScopeType, RoleDefinitionId, RoleName $out.Timestamp = Get-Date -Date $_.EventTimestamp -Format u $out.Caller = "Subscription Admin" if($_.operationName -ieq "Microsoft.Authorization/ClassicAdministrators/write"){ $out.Action = "Granted" } elseif($_.operationName -ieq "Microsoft.Authorization/ClassicAdministrators/delete"){ $out.Action = "Revoked" } $out.RoleDefinitionId = $null $out.PrincipalId = $null $out.PrincipalType = "User" $out.Scope = "/subscriptions/" + $_.SubscriptionId $out.ScopeType = "Subscription" $out.ScopeName = $_.SubscriptionId if($_.Properties -ne $null){ $out.PrincipalName = $_.Properties.Content["adminEmail"] $out.RoleName = "Classic " + $_.Properties.Content["adminType"] } $output += $out } } # end offline events $output | Sort Timestamp } } # End commandlet |