Custom Azure Log from Azure Resource Graph query - powershell

I have Runbook under Azure automation account that should collect results from Resource graph query and pass it to Log analytics as custom log. I have managed to create a script that works fine.
$customerId = "xxxxxxxxx"
$SharedKey = "xxxxxxxxxxxxxxxx"
$LogType = "MyRecord"
$TimeStampField = ""
#function block
Function Connect-ToAzure {
$connectionName = "AzureRunAsConnection"
$automationAccountName = Get-AutomationVariable -Name 'automationAccountName'
Write-Output "Azure Automation Account Name - $automationAccountName"
$connectionName = "AzureRunAsConnection"
Write-Output "Connection Name - $connectionName"
$servicePrincipalConnection = Get-AutomationConnection -Name $connectionName
Write-Output "Logging in to Azure..."
Connect-AzAccount -ServicePrincipal -Tenant $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint `
-Subscription $servicePrincipalConnection.SubscriptionId
}
Function Build-Signature ($customerId, $sharedKey, $date, $contentLength, $method, $contentType, $resource)
{
$xHeaders = "x-ms-date:" + $date
$stringToHash = $method + "`n" + $contentLength + "`n" + $contentType + "`n" + $xHeaders + "`n" + $resource
$bytesToHash = [Text.Encoding]::UTF8.GetBytes($stringToHash)
$keyBytes = [Convert]::FromBase64String($sharedKey)
$sha256 = New-Object System.Security.Cryptography.HMACSHA256
$sha256.Key = $keyBytes
$calculatedHash = $sha256.ComputeHash($bytesToHash)
$encodedHash = [Convert]::ToBase64String($calculatedHash)
$authorization = 'SharedKey {0}:{1}' -f $customerId,$encodedHash
return $authorization
}
# Create the function to create and post the request
Function Post-LogAnalyticsData($customerId, $sharedKey, $body, $LogType)
{
$method = "POST"
$contentType = "application/json"
$resource = "/api/logs"
$rfc1123date = [DateTime]::UtcNow.ToString("r")
$contentLength = $body.Length
$signature = Build-Signature `
-customerId $customerId `
-sharedKey $sharedKey `
-date $rfc1123date `
-contentLength $contentLength `
-method $method `
-contentType $contentType `
-resource $resource
$uri = "https://" + $customerId + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01"
$headers = #{
"Authorization" = $signature;
"Log-Type" = $LogType;
"x-ms-date" = $rfc1123date;
"time-generated-field" = $TimeStampField;
}
Write-Output "Sending a request"
$response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $contentType -Headers $headers -Body $body -UseBasicParsing
return $response.StatusCode
Write-Output "Request has been sent"
}
try {
Write-Output "Starting runbook"
$customerId = "xxxxxxxxx"
$SharedKey = "xxxxxxxxxxxxxxxx"
$LogType = "MyRecord"
$Query = #'
resources
| where type == "microsoft.compute/disks"
| extend diskState = properties.diskState
| extend diskSizeGB = properties.diskSizeGB
| where properties['encryptionSettingsCollection'] != "enabled"
| where diskState == "Attached" or diskState == "Reserved"
| extend diskCreationTime = properties.timeCreated
| extend hostVM = split(managedBy,"/")[-1]
| project diskCreationTime, name, resourceGroup, hostVM, diskState
'#
Connect-ToAzure
$Result = (Search-AzGraph -Query $Query -First 1000)
$jsonResult = $Result | ConvertTo-Json
Post-LogAnalyticsData -customerId $customerId -sharedKey $sharedKey -body $jsonResult -logType $LogType
Write-Output "Runbook has been finished"
}
catch {
Write-Error -Message $_
break
}
However, I have an issue with collection of logs. I gather logs only from one subscription.
Can someone please help with code adjustment? How can I gather results from all subscriptions not just from one? I assume it should be foreach ($sub in $subs), but not sure how to do it combined with this graph query.

As the run as account will retire by Sep'23 so I would recommend using managed identities approach in your runbook i.e., configure role assignment for managed identity across multiple subscriptions as explained here and update your Connect-ToAzure function block something like shown below. For more context with regards to it, refer this Azure document.
$automationAccountName = Get-AutomationVariable -Name 'automationAccountName'
Write-Output "Azure Automation Account Name - $automationAccountName"
Disable-AzContextAutosave -Scope Process | Out-Null
Write-Output "Logging in to Azure..."
$AzureContext = (Connect-AzAccount -Identity).context
Also, you may use UseTenantScope parameter in your Search-AzGraph cmdlet to run the query across all available subscriptions in the current tenant.

Related

How to create a Microsoft Teams team using a PowerShell Azure function and the Graph API?

My ultimate goal is to create a MS Teams team with channels and tabs of applications.
But first, I need to properly format my request. I dont know what I'm doing wrong.
Obviously I found this topic (https://learn.microsoft.com/en-us/graph/api/team-post?view=graph-rest-1.0) Example n°3 that looks promising but I dont know how to use it. I started with the code bellow:
$password = "stackexchange"
$login = "stackexchange#stackexchange.onmicrosoft.com"
$ownerEmail = "stackexchange#stackexchange.onmicrosoft.com"
$url = "https://graph.microsoft.com/v1.0/teams"
$securedPassword = convertto-securestring -String $password -AsPlainText -Force
$creds = new-object -typename System.Management.Automation.PSCredential -argumentlist $login, $securedPassword
$GraphAppId = "stackexchange-guid"
$GraphAppSecret = "stackexchange"
$AADDomain = "stackexchange.onmicrosoft.com"
Connect-AzureAD -Credential $creds
$userId = (Get-AzureADUser -ObjectId $ownerEmail).ObjectId
write-output $userId # Here the userId is actually displayed
Connect-PnPOnline -ClientId $GraphAppId -ClientSecret $GraphAppSecret -AADDomain $AADDomain
$accessToken = Get-PnPGraphAccessToken
$header = #{
"Content-Type" = "application/json"
Authorization = "Bearer $accessToken"
}
$body = #{
displayName = "Test"
"owners#odata.bind" = "https://graph.microsoft.com/v1.0/users('$userId')"
"template#odata.bind" = "https://graph.microsoft.com/v1.0/teamsTemplates('standard')"
memberSettings = #{
allowCreateUpdateChannels = $true
}
messagingSettings = #{
allowUserEditMessages = $true
allowUserDeleteMessages = $true
}
funSettings = #{
allowGiphy = $true
giphyContentRating = "strict"
}
}
$Body = ConvertTo-Json -InputObject $body
Invoke-RestMethod -Uri $url -Body $Body -Method 'Post' -Headers $header -UseBasicParsing -Credential $creds
I get the following message in my PowerShell terminal :
Invoke-RestMethod : {
"error": {
"code": "BadRequest",
"message": "Invalid bind property name owners in request.",
"innerError": {
"date": "2020-09-03T15:40:53",
"request-id": "fef8bd7e-3143-4ea9-bcf6-a87702a488b8"
}
}
}
At character Line:36 : 5
+ Invoke-RestMethod -Uri $url -Body $Body -Method 'Post' -Headers $ ...
+ CategoryInfo : InvalidOperation : (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Instead of doing this all "by hand", I'd suggest looking at the Graph SDK for PowerShell. It's still 'officially' in beta, but that's this PowerShell SDK, not the Graph itself of course.
you could also use the PS nuget "MicrosoftTeams"
for example:
# ===========================================
# this Script creates a new project environment containing:
# - a new TEAMs channel
# ===========================================
Install-Module MicrosoftTeams -Force # -AllowClobber
## parameters
$TeamDisplayName='contoso'
$ProjectName='Contoso-Reporting'
$TEAMS_ChannelName=$ProjectName
## connect to TEAMS
Connect-MicrosoftTeams
## Get the Opslogix TEAM
$team = Get-Team | foreach {if ( $_.DisplayName -eq $TeamDisplayName ) { $_ }}
## create a new project channel
$team | new-TeamChannel -DisplayName $TEAMS_ChannelName
#$team | Get-TeamChannel
## disconnect TEAMS
Disconnect-MicrosoftTeams
Try changing:
"owners#odata.bind" = "https://graph.microsoft.com/v1.0/users('$userId')"
to:
members = #(
#{
'#odata.type' = "#microsoft.graph.aadUserConversationMember"
roles = #(
'owner'
)
'user#odata.bind' = "https://graph.microsoft.com/v1.0/users('$userId')"
}
)

using Invoke-RestMethod to post to a cosmosDB Returns a 400

I am trying to post a document to cosmosdb using powershell, I have followed the steps in this post https://www.systemcenterautomation.com/2018/06/cosmos-db-rest-api-powershell/ and I am still getting a 400 error everytime
Add-Type -AssemblyName System.Web
# generate authorization key
Function Generate-MasterKeyAuthorizationSignature
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true)][String]$verb,
[Parameter(Mandatory=$true)][String]$resourceLink,
[Parameter(Mandatory=$true)][String]$resourceType,
[Parameter(Mandatory=$true)][String]$dateTime,
[Parameter(Mandatory=$true)][String]$key,
[Parameter(Mandatory=$true)][String]$keyType,
[Parameter(Mandatory=$true)][String]$tokenVersion
)
$hmacSha256 = New-Object System.Security.Cryptography.HMACSHA256
$hmacSha256.Key = [System.Convert]::FromBase64String($key)
$payLoad = "$($verb.ToLowerInvariant())`n$($resourceType.ToLowerInvariant())`n$resourceLink`n$($dateTime.ToLowerInvariant())`n`n"
$hashPayLoad = $hmacSha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($payLoad))
$signature = [System.Convert]::ToBase64String($hashPayLoad);
[System.Web.HttpUtility]::UrlEncode("type=$keyType&ver=$tokenVersion&sig=$signature")
}
Function Post-CosmosDocuments{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true)][String]$EndPoint,
[Parameter(Mandatory=$true)][String]$DBName,
[Parameter(Mandatory=$true)][String]$CollectionName,
[Parameter(Mandatory=$true)][String]$MasterKey,
[String]$Verb="POST",
[Parameter(Mandatory=$true)][String]$JSON
)
$Verb = "POST"
$ResourceType = "docs";
$ResourceLink = "dbs/$DBName/colls/$CollectionName"
$dateTime = [DateTime]::UtcNow.ToString("r")
$authHeader = Generate-MasterKeyAuthorizationSignature -verb $Verb -resourceLink $ResourceLink -resourceType $ResourceType -key $MasterKey -keyType "master" -tokenVersion "1.0" -dateTime $dateTime
$header = #{authorization=$authHeader;"x-ms-version"="2017-02-22";"x-ms-date"=$dateTime}
$contentType= "application/json"
$queryUri = "$EndPoint$ResourceLink/docs"
$result = Invoke-RestMethod -Method $Verb -ContentType $contentType -Uri $queryUri -Headers $header -Body $JSON
return $result.statuscode
}
$CosmosDBEndPoint = "https://<omitted>.documents.azure.com:443/"
$DBName = "database"
$CollectionName = "container"
$MasterKey = "<omitted>=="
$SomeObject = [PSObject]#{ id = 1 ; Application = "Ops"; Environment = "Dev"; adKey = "555-555-5555"; }
Post-CosmosDocuments -EndPoint $CosmosDBEndPoint -MasterKey $MasterKey -DBName $DBName -CollectionName $CollectionName -JSON ($SomeObject | ConvertTo-Json)
returns a 400 every time, I am not sure what is wrong with the request.
Based on the Powershell script samples, I see you are missing the headers:
x-ms-documentdb-partitionkey
User-Agent
2018-12-31 as x-ms-version
Adding the response body that you are getting would also help.
it is working now
I am not sure why I was getting a 400. perhaps I the timing on when I created the collection was off.

Cosmos DB Rest API - Create User Permission

I am trying to create a permission for a user on a specific collection.
Ref: https://www.systemcenterautomation.com/2018/06/cosmos-db-rest-api-powershell/
Ref : https://learn.microsoft.com/en-us/rest/api/cosmos-db/create-a-permission
I am able to create the user using the same basic process, but the permissions fail with a
Invoke-RestMethod : The remote server returned an error: (401) Unauthorized.
I know there is a Powershell module out there, but this is in our pipleline so I can't use an unsigned module.
Any Ideas? Key is copy/pasted, and works with the similar create user. I wonder about the Resource Type....
# add necessary assembly
Add-Type -AssemblyName System.Web
# generate authorization key
Function Generate-MasterKeyAuthorizationSignature
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true)][String]$verb,
[Parameter(Mandatory=$true)][String]$resourceLink,
[Parameter(Mandatory=$true)][String]$resourceType,
[Parameter(Mandatory=$true)][String]$dateTime,
[Parameter(Mandatory=$true)][String]$key,
[Parameter(Mandatory=$true)][String]$keyType,
[Parameter(Mandatory=$true)][String]$tokenVersion
)
$hmacSha256 = New-Object System.Security.Cryptography.HMACSHA256
$hmacSha256.Key = [System.Convert]::FromBase64String($key)
$payLoad=$($verb.ToLowerInvariant())`n$($resourceType.ToLowerInvariant())`n$resourceLink`n$($dateTime.ToLowerInvariant())`n`n"
$hashPayLoad =
$hmacSha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($payLoad))
$signature = [System.Convert]::ToBase64String($hashPayLoad);
[System.Web.HttpUtility]::UrlEncode("type=$keyType&ver=$tokenVersion&sig=$signature")
}
function Create-CosmosPermission {
#https://{databaseaccount}.documents.azure.com/dbs/{db-id}/users/{user-name}/permissions
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)][String]$EndPoint,
[Parameter(Mandatory=$true)][String]$DataBaseId,
[Parameter(Mandatory=$true)][String]$CollectionId,
[Parameter(Mandatory=$true)][String]$MasterKey,
[Parameter(Mandatory=$true)][String]$userId,
[Parameter(Mandatory=$true)][String]$collectionLink
)
$Verb = "POST"
$ResourceType = "dbs";
$ResourceLink = "dbs/$DatabaseId/users/$userId/permissions"
$permissionName = "Allow{0}Collection" -f $CollectionId
$dateTime = [DateTime]::UtcNow.ToString("r")
$authHeader = Generate-MasterKeyAuthorizationSignature -verb $Verb - resourceLink $ResourceLink -resourceType $ResourceType -key $MasterKey -keyType "master" -tokenVersion "1.0" -dateTime $dateTime
$header = #{authorization=$authHeader;"x-ms-version"="2017-02-22";"x-ms-date"=$dateTime}
$contentType= "application/json"
$queryUri = "$EndPoint$ResourceLink"
#$queryUri |Out-String
$body =#{
id = $permissionName
permssionMode = "All"
resource = "dbs/$DatabaseId/colls/$collectionId"
}
$JSON = ConvertTo-Json $body
$result = Invoke-RestMethod -Method $Verb -ContentType $contentType -Uri $queryUri -Headers $header -Body $JSON
return $result.statuscode
}
$userId = "testuser"
$dbid ="TestAudit"
$collectionName = "db"
$CosmosDBEndPoint = ""https://mycosmos.documents.azure.com:443/"
$MasterKey = "mycosmoskey"
Create-CosmosPermission -EndPoint $CosmosDBEndPoint -DataBaseId $dbid -CollectionId $collectionName -userId $userId -MasterKey $MasterKey
Please refer to my working code as below:
# add necessary assembly
#
Add-Type -AssemblyName System.Web
# generate authorization key
Function Generate-MasterKeyAuthorizationSignature
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true)][String]$verb,
[Parameter(Mandatory=$true)][String]$resourceLink,
[Parameter(Mandatory=$true)][String]$resourceType,
[Parameter(Mandatory=$true)][String]$dateTime,
[Parameter(Mandatory=$true)][String]$key,
[Parameter(Mandatory=$true)][String]$keyType,
[Parameter(Mandatory=$true)][String]$tokenVersion
)
$hmacSha256 = New-Object System.Security.Cryptography.HMACSHA256
$hmacSha256.Key = [System.Convert]::FromBase64String($key)
$payLoad = "$($verb.ToLowerInvariant())`n$($resourceType.ToLowerInvariant())`n$resourceLink`n$($dateTime.ToLowerInvariant())`n`n"
$hashPayLoad = $hmacSha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($payLoad))
$signature = [System.Convert]::ToBase64String($hashPayLoad);
[System.Web.HttpUtility]::UrlEncode("type=$keyType&ver=$tokenVersion&sig=$signature")
}
# query
Function Post-CosmosDb
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true)][String]$EndPoint,
[Parameter(Mandatory=$true)][String]$DataBaseId,
[Parameter(Mandatory=$true)][String]$CollectionId,
[Parameter(Mandatory=$true)][String]$UserId,
[Parameter(Mandatory=$true)][String]$MasterKey,
[Parameter(Mandatory=$true)][String]$JSON
)
$Verb = "POST"
$ResourceType = "permissions";
$ResourceLink = "dbs/$DatabaseId/users/$UserId"
$dateTime = [DateTime]::UtcNow.ToString("r")
$authHeader = Generate-MasterKeyAuthorizationSignature -verb $Verb -resourceLink $ResourceLink -resourceType $ResourceType -key $MasterKey -keyType "master" -tokenVersion "1.0" -dateTime $dateTime
$header = #{authorization=$authHeader;"x-ms-version"="2017-02-22";"x-ms-date"=$dateTime}
$contentType= "application/json"
$queryUri = "$EndPoint$ResourceLink/permissions"
$result = Invoke-RestMethod -Method $Verb -ContentType $contentType -Uri $queryUri -Headers $header -Body $JSON
return $result.statuscode
}
# fill the target cosmos database endpoint uri, database id, collection id and masterkey
$CosmosDBEndPoint = "https://***.documents.azure.com:443/"
$DatabaseId = "db"
$CollectionId = "coll"
$UserId = "jay"
$MasterKey = "***"
$JSON = #"
{
"id" : "pertest",
"permissionMode" : "All",
"resource" : "dbs/rMYPAA==/colls/rMYPAJiQ3OI="
}
"#
# execute
Post-CosmosDb -EndPoint $CosmosDBEndPoint -DataBaseId $DataBaseId -CollectionId $CollectionId -UserId $UserId -MasterKey $MasterKey -JSON $JSON
Hope it helps you.Any concern,just let me know.

Automation VSTS REST API to create new project

I'm trying to learn how to use REST API, but I'm at square one. The part of not knowing what tool to use to interact with VSTS REST API or how to configure it.
Any help on like "you should use this tool" and "this is how you connect to it".
Small goal is to be able to just get a list of projects in a VSTS account I own and I can build from there.
You can use PowerShell to use REST API of TFS/VSTS easily.
Here is a sample code to list all projects
param(
[Parameter(Mandatory=$true)]
[string] $tfsUri,
[Parameter(Mandatory=$true)]
[string] $token
<#[Parameter(Mandatory=$true)]
[string] $User,
[Parameter(Mandatory=$true)]
[string] $Password#>
)
<# Base64-encodes the Personal Access Token (PAT) appropriately #>
$User=''
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $User,$token)));
$header = #{Authorization=("Basic {0}" -f $base64AuthInfo)};
<#---------------------------------------------------------------------- #>
<#
$securePassword = $Password | ConvertTo-SecureString -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($User, $securePassword)
#>
$reportName = 'TeamProjectList.html'
$teamProjectsReport = '<!DOCTYPE html><html><head>
<!--mce:0-->
</head><body>'
$teamProjectsReport = $teamProjectsReport + '<h2><u><center>' + 'Team Project List' + '</center></u></h2>'
$teamProjectsReport | Out-File -Force $reportName
$teamProjectsReport = '';
$Uri = $tfsUri + '/_apis/projectCollections?api-version=1.0'
$tfsCollections = Invoke-RestMethod -Method Get -ContentType application/json -Uri $Uri -Headers $header #-Credential $credential
foreach($tfsCollection in $tfsCollections.value)
{
$top=100;
$skip=0;
$collectionName = $tfsCollection.name;
if ($tfsUri.Contains('visualstudio.com'))
{
$collectionName = 'DefaultCollection'
}
write-host '==============================='
write-host $tfsCollection.Name
$teamProjectsReport = '<ul><h4>'+ $collectionName + '</h4>';
write-host '-------------------------------'
$collectionProjectList = #();
while($true)
{
$Uri = $tfsUri + '/' + $collectionName + '/_apis/projects?$top='+ $top + '&$skip='+ $skip + '&api-version=1.0'
$tfsProjects = Invoke-RestMethod -Method Get -ContentType application/json -Uri $Uri -Headers $header #-Credential $credential
$skip+=$top;
if($tfsProjects.count -le 0)
{
$orderedProjects = $collectionProjectList| Sort-Object -Property name
foreach($tfsProject in $orderedProjects)
{
write-host $tfsProject.Name
$teamProjectsReport = $teamProjectsReport + '<li> <a target="_blank" href="' + $tfsUri + '/' + $collectionName + '/'+ $tfsProject.Name + '" >' + $tfsProject.Name + '</a></li>';
}
$teamProjectsReport = $teamProjectsReport + '</ul>'
$teamProjectsReport | Out-File -Append -Force $reportName
$teamProjectsReport = '';
break;
}
$collectionProjectList += $tfsProjects.value
}
write-host '-------------------------------'
}
$teamProjectsReport = $teamProjectsReport + '</ul></body></html>'
$teamProjectsReport | Out-File -Append -Force $reportName
To execute this PowerShell in VSTS or TFS 2017 or 2018 you can create a Persona Access Token (PAT) with all scopes (all scopes required here to enable access to collection level information). You can call the script with you VSTS account as shown below.
.\GetAllProjects.ps1 -tfsUri 'https://youraccount.visualstudio.com' -token 'xxxxxxxxPATxxxxxxxxxxxxxx'
To call the script for TFS on premise 2017, 2018 you can use either of below depending on your TFS is setup with SSL.
.\GetAllProjects.ps1 -tfsUri 'https://yourtfs/tfs' -token 'xxxxxxxxPATxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
or
.\GetAllProjects.ps1 -tfsUri 'http://yourtfs:8080/tfs' -token 'xxxxxxxxPATxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Project are listed as below
More information on script available here

Azure DocumentDB Rest API PowerShell delete collection 401 Unathorized

Need to delete collection in my automation process.
Trying to execute script below. Get operations working fine, but Delete operation failed with "(401) Unathorized" error. It is strange cause Delete collection do not need additional headers. Can someone give a hint, what is wrong?
$accountName = 'someaccountname'
$connectionKey = 'masterkey'
$collectionName = 'mycollection'
$databaseName = 'mydatabase'
function GetKey([System.String]$Verb = '',[System.String]$ResourceId = '',
[System.String]$ResourceType = '',[System.String]$Date = '',[System.String]$masterKey = '') {
$keyBytes = [System.Convert]::FromBase64String($masterKey)
$text = #($Verb.ToLowerInvariant() + "`n" + $ResourceType.ToLowerInvariant() + "`n" + $ResourceId + "`n" + $Date.ToLowerInvariant() + "`n" + "`n")
$body =[Text.Encoding]::UTF8.GetBytes($text)
$hmacsha = new-object -TypeName System.Security.Cryptography.HMACSHA256 -ArgumentList (,$keyBytes)
$hash = $hmacsha.ComputeHash($body)
$signature = [System.Convert]::ToBase64String($hash)
[System.Web.HttpUtility]::UrlEncode($('type=master&ver=1.0&sig=' + $signature))
}
function BuildHeaders([string]$action = "get",[string]$resType, [string]$resourceId){
$authz = GetKey -Verb $action -ResourceType $resType -ResourceId $resourceId -Date $apiDate -masterKey $connectionKey
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", $authz)
$headers.Add("x-ms-version", '2015-12-16')
$headers.Add("x-ms-date", $apiDate)
$headers
}
function GetUTDate() {
$date = get-date
$date = $date.ToUniversalTime();
return $date.ToString("r", [System.Globalization.CultureInfo]::InvariantCulture);
}
function GetDatabases() {
$uri = $rootUri + "/dbs"
$hdr = BuildHeaders -resType dbs
$response = Invoke-RestMethod -Uri $uri -Method Get -Headers $hdr
$response.Databases
Write-Host ("Found " + $Response.Databases.Count + " Database(s)")
}
function GetCollections([string]$dbname){
$uri = $rootUri + "/" + $dbname + "/colls"
$hdr = BuildHeaders -resType colls -resourceId $dbname
$response = Invoke-RestMethod -Uri $uri -Method Get -Headers $hdr
$response.DocumentCollections
Write-Host ("Found " + $Response.DocumentCollections.Count + " DocumentCollection(s)")
}
function DeleteCollection([string]$dbname){
$uri = $rootUri + "/" + $dbname + "/colls" + "/" + $collectionName
$hdrs = BuildHeaders -action DELETE -resType colls -resourceId $collectionName
$response = Invoke-RestMethod -Uri $uri -Method Delete -Headers $hdrs
Write-Host "DELETE $uri"
}
$rootUri = "https://" + $accountName + ".documents.azure.com"
write-host ("Root URI is " + $rootUri)
#validate arguments
$apiDate = GetUTDate
$db = GetDatabases | where { $_.id -eq $databaseName }
if ($db -eq $null) {
write-error "Could not find database in account"
return
}
$dbname = "dbs/" + $databaseName
$collection = GetCollections -dbname $dbname | where { $_.id -eq $collectionName }
if($collection -eq $null){
write-error "Could not find collection in database"
return
}
Write-Host
$Delete = DeleteCollection -dbname $dbname | where { $_.id -eq $collectionName }
Normally, either the Authorization or x-ms-date header is not set (or the Authorization header with an invalid authorization token), 401 unauthorized error will be returned. I use fiddler to capture the request and check the response, and I find both Authorization and x-ms-date header are set, so it seems that the Authorization header is set to an invalid authorization token. Based on your code, I do some changes, and the function could work fine on my side.
function DeleteCollection([string]$dbname){
$uri = $rootUri + "/" + $dbname + "/colls" + "/" + $collectionName
$collectionName = $dbname + "/colls" + "/" + $collectionName
$hdrs = BuildHeaders -action DELETE -resType colls -resourceId $collectionName
#Write-Host "resourceId $collectionName"
$response = Invoke-RestMethod -Uri $uri -Method Delete -Headers $hdrs
Write-Host "resource $collectionName"
Write-Host "DELETE $uri"
}
The $collectionName should be dbs/{yourdbname}/colls/{yourcollectionname}
This can happen if there are pending operations. Either retry until it succeeds or add a delay before deleting. For us, a half second is enough to assure we never see this again.