I'm receiving this error when attempting to connect to Exchange online when using a service account from my automation account. I think the problem may also be that my $credential variable is not passing into my $session variable, but I can't think of any reason why that would be happening.
Error :
New-PSSession : [outlook.office365.com] Connecting to remote server outlook.office365.com failed with the following
error message : Access is denied. For more information, see the about_Remote_Troubleshooting Help topic.
At line:5 char:12
$session = New-PSSession -ConfigurationName Microsoft.Exchange -Conne ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [New-PSSession],
PSRemotingTransportException
FullyQualifiedErrorId : AccessDenied,PSSessionOpenFailed
Import-PSSession : Cannot validate argument on parameter 'Session'. The argument is null. Provide a valid value for the
argument, and then try running the command again.
At line:6 char:42
Import-Module (Import-PSSession -Session $session -DisableNameCheckin ...
~~~~~~~~
Script :
Param (
[string] $Employee = ""
)
$credential = Get-AutomationPSCredential -Name 'admin#crafthub.onmicrosoft.com'
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri 'https://outlook.office365.com/powershell-liveid/' -Credential $credential -Authentication Basic -AllowRedirection
Import-Module (Import-PSSession -Session $session -DisableNameChecking -AllowClobber) -Global
$mailbox = Get-Mailbox -Identity $Employee
Write-Output $mailbox
Help will be highly appreciated
Yes, it looks like $credential variable is not passing into $session variable.
In your code you have given Azure Automation credential name as "admin#crafthub.onmicrosoft.com" so double check if you have created Azure Automation credential with the exact name "admin#crafthub.onmicrosoft.com" or not.
You may already be aware but just letting you know that you have to provide Office 365 service account's credentials when you create an Azure Automation credential. To create Azure Automation credential, goto Azure Portal -> Your Azure Automation Account -> 'Credentials' tile -> Click on '+ Add a credential' -> Provide a name for Azure Automation credential under 'Name', provide Office 365 service account's name under 'User name', provide Office 365 service account's password under 'Password' and 'Confirm password' -> Click on 'Create'.
Then, provide the name of that Azure Automation credential in your code's Get-AutomationPSCredential command line i.e., $credential = Get-AutomationPSCredential -Name '<Above_Provided_Azure_Automation_Credential_Name>'
If you have already done all this without any issues then I would recommend you to try latest "Exchange Online PowerShell V2" way i.e., import "ExchangeOnlineManagement" Azure Automation module as shown below and then to connect, use Connect-ExchangeOnline cmdlet instead of New-PSSession cmdlet. Later, as you are trying to get mailbox details so use Get-EXOMailbox cmdlet instead of Get-Mailbox cmdlet. For more information w.r.t it, refer this document.
Related
I created a powershell script to connect to my sharepoint site online.
Having enabled two-factor authentication, I set a password for the app for authentication and used it in the Connect-PnpOnline command.
$securePassword = ConvertTo-SecureString "myappapassword" -AsPlainText -Force
$credentials = New-Object PSCredential ("my#username.com", $securePassword)
Connect-PnPOnline -Url $TenantSiteURL$SiteRelativeURL -Credentials $credentials
On the local computer I don't get any errors, but when I try to run it in Azure Function I get:
Error validating credentials due to invalid username or password
Below are the few workaround to solve the above issue:
Please make sure that you have uploaded your pnp powershell module to Azure function .
By Navigating to Azure function >Advance tool>Kudu console> wwwroot folder > Create new folder copy and paste your modules that have in your local.
Add your service account user name and password in configuration of Azure function.
Try to replace the following cmd in your cmdlt:
$Credential = New-Object System.Management.Automation.PSCredential($serviceAccountEmail, $SecurePassword)
To use managed identity you can refer this Blog
For more information please refer the below links :
MS DOC: Connect-PnpOnine, Granting access via Azure AD App-Only
MS Q&A: Connecting with PnP PowerShell(without username and password)
I have a PowerShell script that logs into Azure subscription with the command Connect-AzAccount using user's credentials.
The code is the following:
$userPassword='password'
$userName="username"
$tenantId="########-####-####-####-############"
$subscriptionId="########-####-####-####-############"
$azureSecpassword = $userPassword | ConvertTo-SecureString -asPlainText -Force
$azureCredential = New-Object System.Management.Automation.PSCredential($userName, $azureSecpassword)
Connect-AzAccount -Credential $azureCredential -Tenant $tenantId -SubscriptionId $subscriptionId
The code above works without any user interaction.
Few days ago the customer enabled the multi-factor authentication for the users.
How can I keep a fully automated login process (without user interactions) with the multi-factor authentication?
Best Regards.
This is a common question. Unfortunately, the answer is No. If the account is MFA-enabled, you could just login with an interactive way.
In such a case, we choose to use the service principal to login with non-interactive in general.
$azureAplicationId ="Azure AD Application Id"
$azureTenantId= "Your Tenant Id"
$azurePassword = ConvertTo-SecureString "client secret" -AsPlainText -Force
$psCred = New-Object System.Management.Automation.PSCredential($azureAplicationId , $azurePassword)
Connect-AzAccount -Credential $psCred -TenantId $azureTenantId -ServicePrincipal
Reference - Sign in with a service principal.
If you must log in as a user, there might be 2 optional approaches.
1. If you will run the script locally or in a specific PC
You can Persist Azure user credentials. You can enable auto save, or manually save the context to a file, and then use it in another PS session.
If you enabled auto save, then you can directly get the context as following:
Get-AzContext
# If you have more than one contexts, you can choose one by specifing the name
Get-AzContext -Name 'CSP Azure (e5b0****-****-****-****-5e5f****4c68) - jack#h****a.onmicrosoft.com'
If you want to manually do it, here is the sample:
# Interactively log for one time
Connect-AzAccount
# Save the context
Save-AzContext -Path D:\ctx.dat
And in another PS session, you can:
Import-AzContext -Path D:\ctx.dat
2. Use refresh token to acquire token, and connect to Azure
You can get the refresh token from the auto saved Azure context (usually at C:\Users\<UserName>\.Azure\TokenCache.dat).
Open the dat file with notepad, and you will get the refresh token:
Then you can get a new token in PowerShell with that refresh token, and connect to Azure:
Clear-AzContext
$tenantId = "e4c9ab4e-****-****-****-230b****57fb"
$subscriptionId = "e5b0fcfa-****-****-****-5e5f****4c68"
$refreshToken = 'AQABAAAAAAAP0****a lot of characters here*****0A9FWoB8mvDtoWRJHBVO7GJzodLKYmNIAA'
$url = "https://login.microsoftonline.com/" + $tenantId + "/oauth2/token"
$body = "grant_type=refresh_token&refresh_token=" + $refreshToken
$response = Invoke-RestMethod $url -Method POST -Body $body
$AccessToken = $response.access_token
Connect-AzAccount -AccountId "the user id, jack#h****a.onmicrosoft.com" -AccessToken $AccessToken -Tenant $tenantId -SubscriptionId $subscriptionId
How can I keep a fully automated login process (without user interactions) with the multi-factor authentication?
You can't do this with a user account--that's the whole point of multi-factor authentication.
Instead, Azure AD supports authenticating with a service principal (instead of a user principal, like you're doing currently), and Azure supports granting access to Azure resources to service principals.
MFA requirements (and other conditional access policies) do not apply to service principals (often referred to as an Azure AD "app"), and service principals support more secure methods of authentication for automation scenarios (e.g. public/private key pairs).
So, what you should do:
Ensure the machine running this script is secure. Anyone with access to the machine has the same amount of access as the script.
Create an application identity and associate credentials with it.
Note: It is strongly recommend you use certificate-based authentication for your service principal, instead of password-based. It is a very insecure practice to have any kind of secret stored in a PowerShell script!
Grant the service principal the minimum level of access to Azure resources, to allow it to complete the required task.
Update your script to use the app's identity (service principal) instead of the user's identity. It's even simpler than using a user account:
$tenantId = "########-####-####-####-############"
$subscriptionId = "########-####-####-####-############"
$appId = "########-####-####-####-############"
$thumbprint= "##############"
Connect-AzAccount -ServicePrincipal -TenantId $tenantId -ApplicationId $appId -CertificateThumbprint $thumbprint
Note: If this script is running on a VM in Azure, you should forget step 2, and simply enable a managed identity and use that.
I am trying to write some powershell script that has to combine both Az cmdlets and AzureRM to accomplish some of the stuff I want to do.
What truly happens though when I call all in the same script:
Connect-AzAccount
Connect-AzureAD
Connect-AzureRMAccount
Initially, I make a call to Get-Credential and save that in a variable.
Then I use those credentials to populate -Credential in the Connect-AzAccount
Then because of Multi-Factor Authentication, I have to then make a call to Connect-AzureAD, which prompts a popup that allows the user to enter Email, Password and Code from MFA text to phone.
Later in the script, there are some cmdlets that are in the RM version, and so I call Connect-AzureRMAccount with the previous Credentials from above.
$credentials = Get-Credential
$azureCredentials = New-Object System.Management.Automation.PSCredential ($credentials.UserName, $credentials.Password)
Connect-AzAccount -Credential $azureCredentials -Tenant $tenantID -SubscriptionId $subscriptionID
Connect-AzureAD -Tenant $tenantID
Connect-AzureRMAccount -Credential $azureCredentials -Tenant $tenantID -SubscriptionId $subscriptionID
What is actually happening in terms of Authentication during this entire script where several different Connect cmdlets are called.
Due to some reason, there is a specific cmdlet
$AppRegistration = New-AzureADApplication -DisplayName $appName -HomePage $AppURI -IdentifierUris $AppURI -ReplyUrls $AppURI -PasswordCredentials $psadCredential
where I get an error in Powershell telling me that I need to call Connect-AzureAD again, even though it was already called once during the script. Does it time out with the MFA?
How can I avoid having to get the user to sign in several times after running the script?
I don't think this is a correct option, if you want to avoid MFA, the workaround is to create a service principal(AD App), grant the permissions for it, then you can login with the service principal without MFA.
You could follow the steps below.
1.Create an Azure Active Directory application, then Upload a certificate and Get values for signing in.
2.Navigate to the Azure Active Directory in the portal -> Roles and administrators -> click Application administrator -> Add assignment -> search by your AD App name(service principal name) -> select it -> Select.
Note: In your case, you want to use the command New-AzureADApplication, so you need to give the Application administrator directory role to your AD App(service principal), if you want to do other things need more permissions, you may need to give a role like Global administrator, it depends on you.
3.Then you could use the command below to login with Az module and AzureAD module.
Connect-AzAccount -CertificateThumbprint "F1D9FE13A8FBxxxx1C8B07D1666" -ApplicationId "aa60b5df-xxxxxx8ae8e0cc2e4" -Tenant "bb58915cxxxxxxb97ed6c65" -ServicePrincipal
Connect-AzureAD -CertificateThumbprint "F1D9FE13A8FBxxxx1C8B07D1666" -ApplicationId "aa60b5df-xxxxxx8ae8e0cc2e4" -Tenant "bb58915cxxxxxxb97ed6c65"
New-AzureADApplication -DisplayName "newapp" -IdentifierUris "http://mynewapp11.contoso.com"
I'm trying to execute this in an Azure Automation runbook
$app = Get-AzureADApplication -ObjectId $ApplicationId
$appRole = New-Object Microsoft.Open.AzureAD.Model.AppRole
$appRole.AllowedMemberTypes = New-Object System.Collections.Generic.List[string]
$appRole.AllowedMemberTypes.Add("User");
$appRole.DisplayName = $TenantName + " Users"
$appRole.Id = New-Guid
$appRole.IsEnabled = $true
$appRole.Description = "Users of the tenant"
$appRole.Value = $TenantName
$app.AppRoles.Add($appRole)
Set-AzureADApplication -ObjectId $ApplicationId -AppRoles $app.AppRoles
Reading the application works fine, when I print the app variable I can see it's the correct application. Executing the script from my own machine also gives no errors. Yet executing it via the runbook gives me:
Set-AzureADApplication : Error occurred while executing SetApplication
Code: Authorization_RequestDenied
Message: Insufficient privileges to complete the operation.
HttpStatusCode: Forbidden
HttpStatusDescription: Forbidden
HttpResponseStatus: Completed
By now I have given the automation application registration in Azure AD all rights of the Active Directory API. I have also clicked "Grant Permissions". I know it's the correct app registration because the script also invites an external user, when I gave the correct rights on the "Graph Api" that started to work.
I tried out your exact script in a run book and to make it work, I had to add code to "Login as the service principal" just before your PowerShell script. You can see more details here:
Using Azure Run As Account in Azure Automation
On the permissions front, I only gave 1 application permission (i.e. "Read and write all applications") and then clicked "Grant Permissions" as it did need Admin consent. Steps were done by a user with "Global administrator" directory role in my Azure AD.
Here is my final working PowerShell script (copied from edit runbook):
# Get Azure Run As Connection Name
$connectionName = "AzureRunAsConnection"
# Get the Service Principal connection details for the Connection name
$servicePrincipalConnection = Get-AutomationConnection -Name $connectionName
# Logging in to Azure AD with Service Principal
"Logging in to Azure AD..."
Connect-AzureAD -TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
$ApplicationId = "redacted-xxxx-xxxx-xxxx-xxxxxxxe3"
$TenantName = "RohitTenant"
$app = Get-AzureADApplication -ObjectId $ApplicationId
$appRole = New-Object Microsoft.Open.AzureAD.Model.AppRole
$appRole.AllowedMemberTypes = New-Object System.Collections.Generic.List[string]
$appRole.AllowedMemberTypes.Add("User");
$appRole.DisplayName = $TenantName + " Users"
$appRole.Id = New-Guid
$appRole.IsEnabled = $true
$appRole.Description = "Users of the tenant"
$appRole.Value = $TenantName
$app.AppRoles.Add($appRole)
Set-AzureADApplication -ObjectId $ApplicationId -AppRoles $app.AppRoles
Here are screenshots from some other important steps that I followed, which you may or may not have done already.
Create Azure Run As account while creating automation account
Make sure account settings for your automation account has the run as accounts now.
Find the App Registration created for Run as Account and give it permission to read and write all Azure AD applications.
We have two azure resources in the same directory. A webAPI set of APIs behind Azure API Management and an Azure Function. We want the azure function to be able to call the APIs. We've enabled MSI on the azure function as described in How to use managed identities for App Service and Azure Functions. We've created an App Registration in AAD for the API, created a role permission to be accessed. Following Calling your APIs with Azure AD Managed Service Identity using application permissions we run into errors attempting to assign the permission/role to the azure function:
in powershell:
New-AzureADServiceAppRoleAssignment -ObjectId 8XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX -Id 3XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX -PrincipalId 8XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX -ResourceId 9XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
New-AzureADServiceAppRoleAssignment : Error occurred while executing NewServicePrincipalAppRoleAssignment
Code: Authorization_RequestDenied
Message: Insufficient privileges to complete the operation.
HttpStatusCode: Forbidden
HttpStatusDescription: Forbidden
HttpResponseStatus: Completed
At line:1 char:1
+ New-AzureADServiceAppRoleAssignment -ObjectId 8XXXXXX-XXXX-XXXX-XXXX ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [New-AzureADServiceAppRoleAssignment], ApiException
+ FullyQualifiedErrorId : Microsoft.Open.AzureAD16.Client.ApiException,Microsoft.Open.AzureAD16.PowerShell.NewServ
icePrincipalAppRoleAssignment
is giving us a permission error, even when an AAD Admin (member of AAD DC Administrators I think) runs it. Has anyone run into this before? Why is this throwing a permissions error? We have verified that the ids are correct with 3 different people.
The problem you're probably facing is that, despite naming your app registration the same thing as your MSI-enabled app, the two end up representing different service principals in AAD. Using app registrations with MSI isn't currently supported.
Try running the powershell commands using the object id of the MSI identity instead. I was able to get this to work, and granted my MSI-enabled app access to the Graph Api.
Here is the PS I used to assign the GraphApi roles my function app required:
$functionAppName = "My-FANCY-FUNC"
$context = Get-AzureRmContext -ErrorAction SilentlyContinue #this lets you search AAD for func
if(!$context){
$login = Connect-AzureRmAccount | Out-Null
Connect-AzureAD #needed this for Graph API
$context = $login
} else { Write-Host "Login session already established for " $context.Subscription.SubscriptionName }
#get the SP associated with the MSI
$MSIPrincipal = Get-AzureRmADServicePrincipal -SearchString $functionAppName | Where-Object DisplayName -eq $functionAppName
#get the SP associatesd with the MS Graph
$graph = Get-AzureADServicePrincipal -All $true | ? { $_.DisplayName -match "Microsoft Graph" }
#find the target app roles in the graph
$targetRoles = $graph.AppRoles | Where-Object Value -in "Group.ReadWrite.All", "Directory.ReadWrite.All"
#iterate throgh the known roles and add the MSI SP to them
$targetRoles | ForEach-Object {New-AzureADServiceAppRoleAssignment -Id $_.Id -PrincipalId $MSIPrincipal.Id -ObjectId $MSIPrincipal.Id -ResourceId $graph.ObjectId}
I suspect, based on your question, that this line will return more than one entity:
Get-AzureRmADServicePrincipal -SearchString $functionAppName | Where-Object DisplayName -eq $functionAppName
Deleting your extraneous app registration should clear that up