Wait for Active Directory Authentication URL list to update within a Powershell Azure Function - powershell

I need to ensure a reply url is added to a v2 Active Directory App before returning a HTTP response within a Powershell Serverless Function.
Currently I've successfully managed connecting to azure using a service principal, getting the active directory application & updating the authentication list with a new reply url.
This works great but there seems to be some propagation period on completing the job. Everything happens as mentioned in a Powershell Serverless Function & returns a 200 HTTP status when finished.
Once the response (HTTP 200 OK) is received I'm using the Active Directory Authentication Library (ADAL) to log in from some JS app using a full page redirect.
This is where the issue lies, once the Powershell runs & returns the client app tries to login with ADAL but that Active Directory prompts with an error, the supplied url isn't one currently on the authentication list.
I've looked into Start-ThreadJob & Wait-Job but not sure if number one I'm using it correctly or number two it is the best approach.
Example code:
$appId = <ACTIVE_DIRECTORY_APP_ID>
$url = <NEW REPLY URL>
$password = ConvertTo-SecureString -String $env:SERVICE_PRINCIPAL_SECRET -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($env:SERVICE_PRINCIPAL_ID, $password)
Connect-AzAccount -ServicePrincipal -Credential $credential -Tenant $env:TENANT_ID
$app = Get-AzADApplication -ApplicationId $appId
$replyUrlList = $app.ReplyUrls
$replyUrlList.Add($url)
Update-AzADApplication -ApplicationId $appId -ReplyUrl $replyUrlList
$status = [HttpStatusCode]::Created
$body = "URL Added Successfully"
Push-OutputBinding -Name Response -Value ([HttpResponseContext]#{
StatusCode = $status
Body = $body
})
At the moment the AD authentication list is updated anywhere from 1 minute - 5 minutes in some cases. Depending if the function is booting from cold-start.
Should I use a loop to check the AD Application information within the Powershell script?
Should I use job threading & wait job cmdlets?
Maybe throw in a bit of sleep?
Just looking for the best approach here to guarantee the new callback url is 100% added before trying to authenticate with the ADAL library.
Any help would be great!

This is not an answer with a solution. But I think I'm reading something that I have experienced on several occasions.
I've been using python and Hashicorp vault to try and manage tokens/RBAC on applications. But very often it would break because it had not updated yet, due to the propagation from AAD to back end being asynchronous from what I was told.
I even did checks where I used ADAL to loop over the application to verify if it was good. But even then it would still fail on some occasions. Which hurt the automation I was trying to put in place.
Now you are having some issue that seems similar, but instead while adding the reply url to an existing application.
My question for testing is; does the reply URL work when it is supplied upon creation of the application? If so, and testing is 100%, then you are having the same issue.
For me, pre-creation of all necessary properties on applications is what helped me circumvent this annoying issue. As I don't think adding a sleep anywhere is a good way to move forward, and the reply from the API isn't reliable enough to work on.
If pre-creation is not an option, I suppose the sleep timer is probably some way forward. For me, that ended up being 2-5m in some cases. And in some lucky cases 7-30s

Related

Authentication error when using SharePoint Migration Tool PowerShell cmdlets

Server 2012 R2 file share to SharePoint Online migration
I am attempting to automate scheduling some file share synchronization to SharePoint Online using the migration tool, however I get an error that my credentials are incorrect.
The same credentials work using the GUI version of the SPMT so I know they are correct, and these credentials are for the global administrator of 365 so there should absolutely be no permissions issues.
The error that I receive:
Task 7967a651-6a2a-47ed-afcd-6b1567496e7d did NOT pass the parameter validation, the error message is 'Username or password for target site https://tenant.sharepoint.com/sites/FileShareSite is not correct' Migration finished, but some tasks failed! You can find the report and log at X:\log.log
The code I am using:
Import-Module Microsoft.SharePoint.MigrationTool.PowerShell
$SPOUrl = "https://tenant.sharepoint.com/sites/FileShareSite"
$Username = "admin#tenant.onmicrosoft.com"
$Password = ConvertTo-SecureString -String "PasSWorD" -AsPlainText -Force
$SPOCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Username, $Password
Register-SPMTMigration -SPOCredential $SPOCredential -Force -MigrateWithoutRootFolder -PreserveUserPermissionsForFileShare $true -WorkingFolder "X:\log"
Add-SPMTTask -FileShareSource "\\file-server\shares\ShareOne" -TargetSiteUrl $SPOUrl -TargetList "ShareOne" -TargetListRelativePath "/"
Start-SPMTMigration -NoShow
According to the logs, I am seeing 400 response codes, as well as some 'An existing connection was forcibly closed by the remote host.'
Something so simple so I don't know what the problem could be; OS is supported, credentials are correct, URL is correct, all these settings work in the GUI version of the tool.
In the logs I see references to logging into AAD, we do not have AAD on this tenant, I am a little curious to know if that is just semantics or if that is part of the problem. I would have assumed the GUI and the PowerShell module use the same mechanisms behind the scenes. Error happened in AAD login MSAL.Desktop.4.37.0.0.MsalServiceException: ErrorCode: user_realm_discovery_failed Microsoft.Identity.Client.MsalServiceException: Response status code does not indicate success: 400 (BadRequest).
So I figured it out, the issue turned out to be PowerShell using an outdated SSL/TLS cipher. I forced TLS1.2 on the PowerShell session using [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 and it is now working as expected.

Deleting Gmail Emails via Google API using Powershell v2.0

$user = "example#gmail.com"
$pass= "examplepassword"
$secpasswd = ConvertTo-SecureString $user -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ($pass, $secpasswd)
Invoke-RestMethod 'https://www.googleapis.com/gmail/v1/users/me/messages/0' -Method Delete -Credentials $cred
So, my problem here is twofold.
I originally tried using Invoke-WebRequest to delete gmail emails via the Google API with a http delete request. However, this did not work because Powershell 2.0 does not support Invoke-WebRequest.
Thereafter, I turned to attempting to utilize Invoke-RestMethod after experimentation with IMAP and POP3, which both required external dependencies (Adding .dlls to the machines I am working with is not optimal).
Therefore, if someone could show me the appropriate way to delete an email via the Google API in Powershell, I would appreciate it. I have provided some sample code as to what I am working with above. Please excuse any mistakes it may contain, as I am relatively new to Powershell, and my experience remains limited in working with RESTful services.
The GMail API is going to require Oauth2 authentication unless this is a gsuit / domain admin / GMail account in which case you can use a service account for authentication. In either case you cant use login and password.
My powershell knowledge is very limited have you considered doing this directly though the mail server IMAP and SMTP and not using the API. No idea if that's possible or not with powershell
Update:
I was able to do it using Invoke-WebRequest you will still need to get an access token first.
Invoke-WebRequest -Uri "https://www.googleapis.com/gmail/v1/users/me/messages/0?access_token=$accesstoken"-Method Get | ConvertFrom-Json
seams to also work
Invoke-RestMethod -Uri "https://www.googleapis.com/gmail/v1/users/me/messages/0?access_token=$accesstoken"-Method Get
Put up a the code for OAuth on GitHub if your interested: Google Oauth Powershell

Powershell Handling cookie popup without using invoke-webrequest

I am attempting to get a script working that does not use invoke-webrequest. The problem I am having is that when I run the script a popup prompt occurs, the popup consists of the following message;
"Windows Security Warning
To allow this website to provide information personalized for you, will you allow it to put a small file (called a cookie) on your computer?"
with yes no response from user
The code I am executing is the following:
$ParsedHTML = New-Object -com "HTMLFILE"
$webresponse = New-Object System.Net.WebClient
$webresponse.Headers.Add("Cookie", $CookieContainer.GetCookieHeader($url))
$result = $webresponse.DownloadString($url)
$ParsedHTML.IHTMLDocument2_write($webresponse)
$ParsedHTML.body.innerText
The main problem with this code is that the $url I am using part of the weblink checks to see if cookies are enabled and this code causes a returned value of disabled.
My question, is there a way to handle the cookie request without changing the output response from the test url site.
Note: This script will be automating a process over hundreds of remote computers and thus having an unhandled popup will just prevent the script from running.
I found the answer in another SO question Using Invoke-Webrequest in PowerShell 3.0 spawns a Windows Security Warning
add the parameter -UseBasicParsing
Technet https://technet.microsoft.com/en-us/library/hh849901.aspx notes that the parameter stops the DOM processing. and has the caveat "This parameter is required when Internet Explorer is not installed on the computers, such as on a Server Core installation of a Windows Server operating system."
So, you mileage may vary.

Add-AzureAccount : Index was out of range. Must be a non-negative and less than the size of the collection

On 4th of August a new version of the Azure powershell module (0.8.7.1) was released. In it is the ability to create credentials which you could then pass to the Add-AzureAccount function. Add-AzureAccount allows you to pull in an account to work with in the current PowerShell session.
$userName = "buildmaster#someaccount.onmicrosoft.com"
$securePassword = ConvertTo-SecureString -String "somepassword." -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential($userName, $securePassword)
Add-AzureAccount -Credential $cred
This allowed me to get away from a popup window or messing with a settings file.
It seems to have stopped working! Both Add-AzureAccount (which pops up a window) and the credential based way. They now return an index was out of range issue.
Add-AzureAccount : Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
At S:\QA\Azure Scripts\cm-azure-helpers.psm1:1128 char:5
+ Add-AzureAccount
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Add-AzureAccount], ArgumentOutOfRangeException
+ FullyQualifiedErrorId : Microsoft.WindowsAzure.Commands.Profile.AddAzureAccount
Digging deeper with Fiddler shows that the OAuth call to the back end service seems to work. I get a token returned. But then I also get a 302 in the middle of the process stating that the page has moved. I don't know if that redirect was there when it was working previously or not.
Has anyone else experienced the Add-AzureAccount function just stop working like this? And more importantly - have you found a way around it?
Update - More info
I have now tried on several boxes under different azure accounts and seem to have the same results. I get a valid auth token returned with a redirect in the middle (not sure if that is an issue or not) and then get the index was out of range.
I have done this with the following variations:
PS:3 Azure Module: 0.8.6
PS:3 Azure Module: 0.8.7
PS:4 Azure Module: 0.8.6
PS:4 Azure Module: 0.8.7
I know exactly what caused this error for me, and how I worked around it (I thought I was the only one who had seen this :))
What had happened is that I had accidentally added a bogus/empty subscription to my account. And this empty subscription had been set to my "default" subscription.
Run "get-azuresubscription -default" to see what your default subscription is. You can then "remove" any junk subscriptions using "remove-azuresubscription" command.
You can then of course set a new azure subscription for your "default" using PS.
I actually reported this to the Azure PowerShell team now to get a better error message during this scenario.
Hopefully this solved your problem, it's possible other errors manifest the same error message.
If you do a fiddler trace, you should see that right after the login call (where PowerShell passes in your username/password, there should be 1 or multiple calls to GET /subscriptions.
Check the response to see whether there is anything suspicious there. Like, any of them return an empty body, empty array, subscription with id, name, etc..
I had a similar problem recently. The "fix" was for me was to delete the files at C:\Users[username]AppData\Roaming\Windows Azure Powershell (esp. the WindowsAzureProfile.xml file). The next time I ran Add-AzureAccount, the necessary files were created and all was well.
Please use:
Add-AzureRmAccount -SubscriptionId "id";
for login

Validate Service Account Powershell

I want to write a Powershell script that will validate a large number of service accounts that was provided to me by my AD team. Not that I don't trust them but I want to cycle thru each domain username and password to see if it logs in or fails. I am looking for some suggestions so far my attempts have failed (see post http://tjo.me/fKtvPM).
Thanks
P.S. I don't have access to AD so I have to try to login using the credentials to test.
This is really hacky (ugly for least-privileged model), but if you know that all of the service accounts have access to a particular program / file, you can try to start a process using their credentials.
$cred = get-credential # however you're getting the info from AD team, pass it hear to get-credential
start-process powershell -argumentlist "-command","exit" -cred (get-credential)
$? # if $true, process started (and exited) successfully, else failed (either bad creds or account can't access powershell.exe
Unfortunately, since you can't query AD directly, I think any solution is going to be a bit of a hack, since by definition you're going to have to simulate logging in as the user account.