I created an application in azure active directory like below
image
I am trying to add a Storage Blob Data Contributor role assignment for azure active directory service principal to operate on the storage account level through PowerShell
whenever i try to run the below command
New-AzRoleAssignment -Serviceprincipal <Serviceprincipal name> -RoleDefinitionName "Storage Blob Data Contributor" -Scope "/subscriptions/<subscriptionsID>/resourceGroups/<resourcegrp name>/providers/Microsoft.Storage/storageAccounts/<storageacc name>"
I am getting the error as below.please help me with the solution
New-AzRoleAssignment: Operation returned an invalid status code 'NotFound'
I tried to reproduce the same in my environment I got same error like below.
To resolve this issue check whether you have provided correct endpoints and service principal ID try to use this scope in storage account endpoint like below.
In your storage account -> Under setting -> Endpoint -> Storage account resource
When I try to run the below command, I got result successfully.
New-AzRoleAssignment -Serviceprincipal <Serviceprincipal name> -RoleDefinitionName "Storage Blob Data Contributor" -Scope "/subscriptions/<subscriptionsID>/resourceGroups/<resourcegrp name>/providers/Microsoft.Storage/storageAccounts/<storageacc name>"
To check in portal, finally Storage Blob Data Contributor role is added successfully like below.
Related
I am using a automation account runbook to compare files within a storage account fileshare and have been trying to use Get-AzStorageFileContent to download them so I can then compare.
However, I get the error: "Get-AzStorageFileContent : Can not find your azure storage credential. Please set current storage account using "Set-AzSubscription" or set the "AZURE_STORAGE_CONNECTION_STRING" environment variable."
When I google "Set-AzSubscription" it doesn't appear to exist but I am directed to Set-Azcontext which I have tried to use to set the context to the subscription the storage account is in but this produces the either the same error when testing in powershell ISE or the erorr "please provide a valid tenant or a valid subscription" in the runbook (even though I am using the correct IDs for both)
I have noticed that the storage account is in a different subscription to the runbook could this be breaking it? It does allow me to save files to the storage in the same script so I'm not sure why it would break here.
I am authenticating with a managed identity if that's relevant.
My code to get the file looks like this:
try{
write-output "get file"
Set-Azcontext -Subscription "--storage account subscription--" -Tenant "--Our tenant--"
Get-AzStorageFileContent -ShareName "--storage account name--" -Path "--path of file--"
}
catch{
Write-Output "couldn't get file"
Write-Warning $_.Exception.Message
break
}
Get-AzStorageFileContent : Can not find your azure storage credential. Please set current storage account using "Set-AzSubscription" or set the "AZURE_STORAGE_CONNECTION_STRING" environment variable:
I also got the same error when I tried in my environment.
This issue usually occurs if you do not create a storage context by specifying a storage account name and storage account key, which is required for storage account authentication.
I tried below script in Azure runbook, and it worked successfully as detailed below:
Connect-AzAccount
Set-AzContext -Subscription "<SubscriptionID>"
$context = New-AzStorageContext -StorageAccountName "<StorageAccountName>" -StorageAccountKey "<StorageAccountKey>"
Get-AzStorageFile -ShareName <FileshareName> -context $context
Output:
I've run into a snag with my powershell script that builds an azure function & all its dependencies.
This is what's happening: (i'm doing it manually here to demo...)
I request the storage account information like this:
PS C:\Users\me\> Get-AzStorageAccount -ResourceGroupName widget-resource-group
StorageAccountName ResourceGroupName PrimaryLocation SkuName Kind AccessTier CreationTime ProvisioningState EnableHttpsTrafficOnly LargeFileShares
------------------ ----------------- --------------- ------- ---- ---------- ------------ ----------------- ---------------------- ---------------
widgetx4ge6v27rlgdk widget-resource-group eastus Standard_LRS StorageV2 Hot 2022-03-10 2:00:26 PM Succeeded True
It comes back with the correct information. So then I try to get the connection string like this:
PS C:\Users\me> func azure storage fetch-connection-string widgetx4ge6v27rlgdk
Cannot find storage account with name widgetx4ge6v27rlgdk
But it says it can't find the storage account.
The actual code looks like this:
# Look up function app name that was dynamically created by ARM template:
$AZ_FUNCTION_APP = Get-AzFunctionApp -ResourceGroupName $currentEnv.AZ_RESOURCE_GROUP_NAME
#look up the storage account name for this resource group.
$AZ_STORAGE_ACCOUNT = Get-AzStorageAccount -ResourceGroupName $currentEnv.AZ_RESOURCE_GROUP_NAME
Write-Output $AZ_STORAGE_ACCOUNT.StorageAccountName
# Get new connection string for the storage account.
func azure storage fetch-connection-string $AZ_STORAGE_ACCOUNT.StorageAccountName
When the code runs, everything works until the call to "func azure storage fetch-connection-string".
Any tips on what I'm missing?
Edit 1
In case it helps, this logic works just fine when I run it against Tenant 1, Subscription A. But for Tenant 1, Subscription B it bombs.
I've made sure the service account principle it runs under is contributor on both subscriptions.
And for what it's worth, the script is able to create the resource group and many of the resources inside. It's just hat when I try to get the connection string, it bombs. It also bombs further down in the script when it tries to deploy the functions in my function app. The error message though is similar - it complains that it can't find the function app that I just finished creating.
Edit 2
So I figured out the problm but not sure how to fix it in a nice / simple way.
For 90% of the script, including login, i'm using the new Az Powershell modules. However, the "func azure" tool relies on login information provided by the az cli. (that seems to be cached??)
To get you on the same page, here's the relevant part of the code in the script:
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $AZ_DEPLOYMENT_CLIENT_ID, $Secure2
#Connect
Connect-AzAccount -ServicePrincipal -TenantId $AZ_TENANT_ID -Credential $Credential
#OPTIONAL - List the subscriptions available to the current User
Get-Azcontext -ListAvailable
#Set the subscription context to subscription 2
Set-Azcontext -Subscription $AZ_SUBSCRIPTION_ID -Tenant $AZ_TENANT_ID
#Create a new resource group
New-AzResourceGroup -Name $AZ_RESOURCE_GROUP_NAME -Location $AZ_RESOURCE_LOCATION -Force
New-AzResourceGroupDeployment -ResourceGroupName $AZ_RESOURCE_GROUP_NAME -TemplateFile (Join-Path $PSScriptRoot "./artifacts/widget-resources.json")
# Look up function app name that was dynamically created by ARM template:
$AZ_FUNCTION_APP = Get-AzFunctionApp -ResourceGroupName $AZ_RESOURCE_GROUP_NAME
#look up the storage account name for this resource group.
$AZ_STORAGE_ACCOUNT = Get-AzStorageAccount -ResourceGroupName $AZ_RESOURCE_GROUP_NAME
Write-Output $AZ_STORAGE_ACCOUNT.StorageAccountName
# this is where it is failing because it is using a subscription that is visible to az cli.
func azure storage fetch-connection-string $AZ_STORAGE_ACCOUNT.StorageAccountName
Here's what I did to troubleshoot from a powershell cli:
az account list
That returns this:
{
"cloudName": "AzureCloud",
"homeTenantId": "asdf-asdf-asdf-asdf-12312312313",
"id": "[guid]",
"isDefault": false,
"managedByTenants": [],
"name": "subscription-1",
"state": "Enabled",
"tenantId": "[our-tenant-id]",
"user": {
"name": "[another-guid]",
"type": "servicePrincipal"
}
}
When I ran the above command, it only returned one subscription called "subscription-1" for discussion purposes. It isn't/wasn't the one that the rest of the script was working with. The rest of script was dealing with subscription 2
As I test, I added the following lines of code just before call func azure storage:
az login --service-principal --username $AZ_APPLICATION_CLIENT_ID --password $AZ_SECRET --tenant $AZ_TENANT --allow-no-subscriptions
#set the subscription we want to use
az account set --subscription $subscription2
func azure storage fetch-connection-string $AZ_STORAGE_ACCOUNT.StorageAccountName
And now it finds the correct subscription and resource group / storage account. And now when I run az account list again, it shows me both subscriptions.
One addition comment / observation. Once the az login / az account set has been run with the desired subscription id, i've noticed that I can remove the az login and account set logic from the script and it just uses the cached values. I'm not saying this is what I want to do ... cuz I think it' best to be explicit. But just an observation which explains what bit me in the first place.
So my question is... is there anyway to avoid having to log in twice - once with az cli and another time with the Az Powerhsell modules?
I'm thinking of just abandoning the Az Powershell module and just rewriting everything in just az cli.
But asking the community to see if there's a better way to do this.
EDIT 3
Based on the docs for the azure core functions tools, technically I should be able to use the powershell modules or the cli:
https://learn.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=v4%2Cwindows%2Ccsharp%2Cportal%2Cbash#publish
"You must have the Azure CLI or Azure PowerShell installed locally to be able to publish to Azure from Core Tools."
Yes, using a mix of azcli and azure powershell, as they are their seperate entities in their own right, you would need to login to each of them individually.
And yes, you are right its better to ditch of them and choose one or the other ! Just much cleaner that way
The issue was that the azure core functions tool is using the cached az account list to find my resources.
So in other words, unbeknownst to me, the func method was using az cli, whereas the rest of the script is using the new Az Powershell modules.
For now, I've just rewritten everything in az cli syntax, and am happy with that. But per the docs it seems that the azure core functions tools should be able to work with either az cli or az powershell. Will open a separate question that addresses that point. For now, my script is working again.
I am defining a custom azure policy by using this azure template (official github repo of azure).
As a parameter, i am just passing the log analytics workspace name in the param. The below powershell code (taken from the azure repo) is used for that purpose, i have just added -AssignIdentity to the second command as it is necessary. As a role definition, i give owner of subscription rights inside the template.
$definition = New-AzPolicyDefinition -Name "deploy-oms-vm-extension-windows-vm" -DisplayName "Deploy default Log Analytics VM Extension for Windows VMs." -description "This policy deploys the Log Analytics VM Extensions on Windows VMs, and connects to the selected Log Analytics workspace." -Policy 'https://raw.githubusercontent.com/Azure/azure-policy/master/samples/Compute/deploy-oms-vm-extension-windows-vm/azurepolicy.rules.json' -Parameter 'https://raw.githubusercontent.com/Azure/azure-policy/master/samples/Compute/deploy-oms-vm-extension-windows-vm/azurepolicy.parameters.json' -Mode Indexed
$definition
$assignment = New-AzPolicyAssignment -Name <assignmentname> -Scope <scope> -logAnalytics <logAnalytics> -PolicyDefinition $definition -AssignIdentity
$assignment
The policy is created correctly. But when i try to create a remediation task, the task fails and i get the error below:
Details
Code AuthorizationFailed
Message The client 'xxxx-xxx-xxxx-xxxx-xxxx' with object id 'xxxx-xxx-xxxx-xxxx-xxxx' does not have authorization to perform action 'Microsoft.Resources/deployments/validate/action' over scope '/subscriptions/xxxx-xxx-xxxx-xxxx-xxxx/resourcegroups/rg-test/providers/Microsoft.Resources/deployments/PolicyDeployment_17825756917269472742' or the scope is invalid. If access was recently granted, please refresh your credentials.
On the portal, i see that the policy definition has owner rights as i have defined.
But i also see this on the policy remediation page:
I don't understand the reason of this error. Does someone have any idea?
According to some test, it may be caused by your account permission. I don't think it has anything to do with the role of your policy definition.
One case to show the same error message is do the operation with wrong subscription or wrong resource group, but I think it's a very low probability to choose wrong subscription or resource group because you do it on portal.
The other case to show this error message is what I test in my side. I test with one account hasn't be assigned a role which have enough permission, it shows same error message. So please check your account role and assign a role with higher permission, then create remediation task.
I have found a solution. So the managed identity created during the New-AzPolicyAssignment command execution, is not created with the right permission. The workaround solution is like this:
$definition = New-AzPolicyDefinition -Name "deploy-oms-vm-extension-windows-vm" -DisplayName "Deploy default Log Analytics VM Extension for Windows VMs." -description "This policy deploys the Log Analytics VM Extensions on Windows VMs, and connects to the selected Log Analytics workspace." -Policy 'https://raw.githubusercontent.com/Azure/azure-policy/master/samples/Compute/deploy-oms-vm-extension-windows-vm/azurepolicy.rules.json' -Parameter 'https://raw.githubusercontent.com/Azure/azure-policy/master/samples/Compute/deploy-oms-vm-extension-windows-vm/azurepolicy.parameters.json' -Mode Indexed
$assignment = New-AzPolicyAssignment -Name <assignmentname> -Scope <scope> -logAnalytics <logAnalytics> -PolicyDefinition $definition -AssignIdentity
## Get newly created policy assignment object
$PolicyAssignment = Get-AzPolicyAssignment -Name $assignmentname -Scope $scope
## Extract the RoleID and ObjectID
$roleDefinitionId = [GUID]($definition.properties.policyRule.then.details.roleDefinitionIds -split "/")[4]
$objectID = [GUID]($PolicyAssignment.Identity.principalId)
## Create a role assignment from the previous information
New-AzRoleAssignment -Scope $scope -ObjectId $objectID -RoleDefinitionId $roleDefinitionId
There is a already opened issue here. I have adapted the solution from there.
Using Powershell in an Azure DevOps pipeline, I am trying to assign the key vault's principal the role Storage Account Key Operator Service Role to a storage account.
Command Line
The command line is run after I connected Azure with the service principal:
$credentials = New-Object -TypeName System.Management.Automation.PSCredential($servicePrincipalApplicationId, $clientSecret)
Connect-AzAccount -ServicePrincipal -Credential $credentials -Tenant $tenantId
Here is the command line that I execute :
New-AzRoleAssignment -ApplicationId $keyVaultServicePrincipalId -ResourceGroupName $resourceGroupName -ResourceName $storageAccountName -ResourceType "Microsoft.Storage/storageAccounts" -RoleDefinitionName "Storage Account Key Operator Service Role"
Where:
$keyVaultServicePrincipalId is the pre-registered principal ID for Key Vault. Its value is cfa8b339-82a2-471a-a3c9-0fc0be7a4093.
$resourceGroupName is the name of the resource group in which the storage is located. Its value is accountsmanager-test-global-rg.
$storageAccountName is the name of my storage account. Its value is accountsmanagertest.
Service Principal
Here are the permission of the service principal under which the command is run:
The command is run as a Service Principal that has the Owner role in the subscription:
The resource group created in that subscription is also owned by that Service Principal:
Question
When I run the command, I get the following error:
New-AzRoleAssignment: The provided information does not map to an AD object id.
Why do I get the error The provided information does not map to an AD object id. when executing the command New-AzRoleAssignment?
I can also reproduce this on my side, there are two issues.
1.In your command, the ResourceType should be Microsoft.Storage/storageAccounts, not Microsoft.Storage/storageAccount.
2.In the API permission of your AD App related to the service principal used in the DevOps servcie connection, you need to add the Application permission Directory.Read.All in Azure Active Directory Graph, not Microsoft Graph.
After a while to take effect, it will work fine.
I am trying to get the Key of a Storage Account from inside a Powershell Function App under the same Resource Group "rg-mobileplans".
I am certain I have the correct Azure Context and when listing all the Storage Accounts I see the one I am trying to retrieve the key from - "stmobileplansstaging".
Write-Host ((Get-AzContext).Subscription)
Write-Host (Get-AzStorageAccount -ResourceGroupName rg-mobileplans).StorageAccountName
Output:
<Subscription ID Hidden for Privacy>
stmobileplansstaging storageaccountrgmob83d8
But when I try to get the key itself I get an error message that the Storage Account Could not be found.
Get-AzStorageAccountKey -ResourceGroupName rg-mobileplans -AccountName stmobileplansstaging
Output:
ERROR: Get-AzStorageAccountKey : The Resource 'Microsoft.Storage/storageAccounts/stmobileplansstaging ' under resource group 'rg-mobileplans' was not found. For more details please go to https://aka.ms/ARMResourceNotFoundFix
Keep in mind that I am running these commands from a Function App. Running them from Powershell works.
I have created a System Managed Identity for the Function App and gave it "Owner" access to the entire "rg-mobileplans" Resource Group and the "stmobileplansstaging" Storage Account.
What am I missing?
I just realized what the problem is. In the script the storage account name is given through a tag. The name had a space at the end. When running the get / list storage account commands it's OK and the commands succeed because the name of the account is probably at the end, but for getting the key, underneath a POST call is being made and the URL looked something like "http://.../storage/stmobileplansstaging /rest-of-the-uri". Notice the space in the middle of the URI which broke everything.