EWS and AutoDiscoverURL error using Azure AD Certificate with Powershell - powershell

I've tried with and without Secret ID, and now with a self-signed Certificate and I keep getting the same error:
Exception calling "AutodiscoverUrl" with "2" argument(s): "The
expected XML node type was XmlDeclaration, but the actual type is
Element."
My PowerShell script:
$TenantId = "blahblah"
$AppClientId="blahblah"
$EDIcertThumbPrint = "blahblah"
$EDIcert = get-childitem Cert:\CurrentUser\My\$EDIcertThumbPrint
$MsalParams = #{
ClientId = $AppClientId
TenantId = $TenantId
ClientCertificate = $EDIcert
Scopes = "https://outlook.office.com/.default"
}
$MsalResponse = Get-MsalToken #MsalParams
$EWSAccessToken = $MsalResponse.AccessToken
Import-Module 'C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll'
#Provide the mailbox id (email address) to connect via AutoDiscover
$MailboxName ="email#myemaildomain.com.au"
$ews = [Microsoft.Exchange.WebServices.Data.ExchangeService]::new()
$ews.Credentials = [Microsoft.Exchange.WebServices.Data.OAuthCredentials]$EWSAccessToken
$ews.Url = "https://outlook.office365.com/EWS/Exchange.asmx"
$ews.AutodiscoverUrl($MailboxName,{$true})
I've searched that error message everywhere, and I am not getting anywhere. The error doesn't make sense, because I am not referring to XML in any way - unless it's embedded inside the EWS?
The only time this works is when I don't use either a Secret ID nor a Certificate, but the Token only lasts 1 hour! I need to make this automatic, so I can get into my mailbox and extract files from emails.
Thanks
UPDATE
So I've removed the AutoDiscoverUrl() and I now getting another error:
Exception calling "FindItems" with "2" argument(s): "The request
failed. The remote server returned an error: (403) Forbidden."
Trace log:
The token contains not enough scope to make this call.";error_category="invalid_grant"
But why when I have an Oauth token!?
My code in trying to open the "Inbox":
$results = $ews.FindItems(
"Inbox",
( New-Object Microsoft.Exchange.WebServices.Data.ItemView -ArgumentList 100 )
)
$MailItems = $results.Items | where hasattachments

AutoDiscoverv1 doesn't support the client credentials flow so you need to remove the line
$ews.AutodiscoverUrl($MailboxName,{$true})
It's redundant anyway because your already setting the EWS endpoint eg
$ews.Url = "https://outlook.office365.com/EWS/Exchange.asmx"
The only time that endpoint would change is if you had mailbox OnPrem in a hybrid environment and there are other ways you can go about detecting that such as autodiscoverv2.

Related

Teams Webhook error: Invoke-RestMethod: Bad payload received by generic incoming webhook

I'm using configured Teams webhook in my Powershell script and keep encountering the mentioned error message. What's strange, is that this exact method of configuring Webhook worked a few months ago on a different script.
Here's what I'm trying to do:
#Set URI of the Teams channel Webhook
$URI = 'https:....'
#Define Rest Method Parameters for the Teams Webhook sending
$RestMethodParameters = #{
"URI" = $URI
"Method" = 'POST'
"Body" = $null
"ContentType" = 'application/json'
}
$JSONBody = #{
"#type" = "MessageCard"
"#context" = "http://schema.org/extensions"
"themeColor" = '0078D7'
}
#Adding text to title and body
$JSONBody += #{
"title" = "'costReport-func' Function for connecting AzAccount has failed"
"text" = "Function failed at connection to AzAccount step."
}
#Sending the message to Teams
($RestMethodParameters).Body += ConvertTo-Json $JSONBody
Invoke-RestMethod #RestMethodParameters
And with this I'm getting "Bad payload received by generic incoming webhook." error message. What is causing the issue here?
Update: Microsoft has released a preview version (2.1.0) of the Teams PowerShell module which works properly with modern authentication. It’s likely that this version will be pushed through to general availability quite quickly.
Please go through this link for more information.

Getting restrictions from Confluence page

I'm not very savvy with web API calls, but I've been using the following powershell code (this site in this example is one I found that has some public data... my site is internal and requires I pass the credential, which has been working for me without issue):
If(-not (Get-InstalledModule -Name 'ConfluencePS')){Install-Module ConfluencePS}
Import-Module ConfluencePS
Set-ConfluenceInfo -BaseUri "https://wiki.opnfv.org"
$space = Get-confluencepage -Spacekey ds
ForEach($item in $space)
{
$splatParams = #{
Uri = "https://wiki.opnfv.org/rest/api/content/$($item.ID)/restriction"
ContentType = 'application/json'
method = 'GET'
}
#reference https://developer.atlassian.com/cloud/confluence/rest/#api-api-content-id-restriction-get
Invoke-RestMethod #splatParams
}
The documentation for the ConfluencePS shows that restrictions is still an open feature request but I need to get this working for a project.
I put a breakpoint in on line 982 from ConfluencePS.psm1 and was able to see the various calls and how the params are structured but when I try to mimic it (and change the URI based on the confluence documentation) I get an error "HTTP error 405 - MethodNotAllowed". Anyone have suggestions on how I can get this working? I'm trying to return back the permissions applied for all pages in a specific space.
Get Restrictions by Content ID
As you found out by yourself, it is required to add "byOperation".
I was able to get the restrictions of a specific page with the following code:
# for testing purposes ONLY, I've specified the URL and ID
$wikiUrl = "https://wiki.opnfv.org"
$itemId = "6820746"
$splatParams = #{
Uri = "$wikiUrl/rest/api/content/$itemId/restriction/byOperation"
ContentType = 'application/json'
method = 'GET'
}
$result = Invoke-RestMethod #splatParams
Tested on version 6.0.4 and 6.15.9
Filter by user name
If you like to filter the result by a specific username, you can use the following URI:
"$wikiUrl/rest/api/content/$itemId/restriction/byOperation/.../user?userName=".
Bt, there's an open bug on this way of action:
restriction returns ambiguous responses

How to remove a permission, a member, or a role?

I have this script below to add roles and members and permissions
Import-Module sqlserver
$Server = new-Object Microsoft.AnalysisServices.Tabular.Server
$Server.Connect("SERVER\INSTANCE")
$TabDB = $SERVER.Databases["DATABASENAME"]
$AddRole = new-Object Microsoft.AnalysisServices.Tabular.ModelRole
$AddRole.Name = 'NewRole1'
$AddRole.ModelPermission="Read"
$RoleMember = New-Object Microsoft.AnalysisServices.Tabular.WindowsModelRoleMember
$RoleMember.MemberName = 'DOMAIN\ACCOUNT'
$TabDB.Model.Roles.Add($AddRole)
$AddRole.Members.Add($RoleMember)
$TabDB.Update([Microsoft.AnalysisServices.UpdateOptions]::ExpandFull)
$server.Disconnect()
How can I remove a permission, a member, and a role?
I tried this at least for the role, but the console returns back "False"
$TabDB.Model.Roles.Remove($AddRole)
When working with ssas from powershell (or C# for that matter) you can use the analysisservices namespace of microsoft:
Microsoft analysisservices.
This is an object oriented way of working with ssas databases.
This is an old script I wrote for maintainers:
function addRoleToDb {
$roleName = Read-Host "How should this role be called?"
if ($global:dataBase.Roles.findByName($roleName)) {
echo "This role already exists"
return
} elseif ($roleName -eq "") {
echo "You can't give an empty name for a role."
return
}
echo "Warning: this role will start out empty and will have to be modified in order to be used. (it wil have read permission)"
[Microsoft.AnalysisServices.Role] $newRole = New-Object([Microsoft.AnalysisServices.Role])($roleName)
$global:dataBase.Roles.add($newRole)
$dbperm = $global:dataBase.DatabasePermissions.add($newRole.ID)
$dbperm.Read = [Microsoft.AnalysisServices.ReadAccess]::Allowed
$global:dataBase.Roles.Update()
$dbperm.Update()
return
}
At this point I already had a global variable database.
This is a method that would add a role to the database. Deleting the database would work practically the same, you would get the instance of the role with:
role = database.roles.findbyname()
or
role = database.roles.findbyid()
and then
role.Drop(DropOptions.AlterOrDeleteDependents);
You should check that last line before using it because the alterordeletedependants is something I now use in my c# program, I don't remember if it worked in powershell.
Good luck!

problems with paypal module

is anyone ever tried this module NET::PayPal
because i tried it and i was getting this error
Authorization failed : 401 Unauthorized, {"error":"invalid_client","error_description":"Client Authentication failed"}
and am using valid client id and secret id
use Net::PayPal;
$client_id = 'asdasdasdasdsadasdasd';
$client_secret = 'sadasdasdsadasdsad';
my $p = Net::PayPal->new($client_id, $client_secret);
my $payment = $p->get_payment( 'PAY-9D023728F47376036KE5OTKY' );
According to the documentation, Net::PayPal always starts out in sandbox mode. You have to switch it to live mode in order to use your production credentials.
Net::PayPal->live( 1 );
my $pp = Net::PayPal->new($client_id, $secret);

Powershell - Querying URL with login and returning HTTP Status

I'm using a standard [System.Net.WebRequest] class to return the HTTP response code of a given URL.
The URL points to an internal web application server, which returns a "401 Unauthorised" error. This is actually OK as the service account running the script doesn't have a valid account to the application. However, I am more interested that the website is responding. However, I assumed that this is a HTTP Response in itself so I could manage this, but instead it returned as a null value.
$HTTP_Request = [System.Net.WebRequest]::Create('http://google.com')
$HTTP_Response = $HTTP_Request.GetResponse()
$HTTP_Status = [int]$HTTP_Response.StatusCode
Exception calling "GetResponse" with "0" argument(s): "The remote
server returned an error: (407) Proxy Authentication Required."
(I'm using Google in this example, which our servers are blocked from accessing external sites).
So I can't get as far as $HTTP_Status = [int]$HTTP_Response.StatusCode in the code because it won't accept 400 errors as a code.
How can I accept a 401 (or 407 in this example) in the query?
Thanks
Got it!
try{
$request = $null
$request = Invoke-WebRequest -Uri "<URL>"
}
catch
{
$request = $_.Exception.Response
}
$StatusCode = [int] $request.StatusCode;
$StatusDescription = $request.StatusDescription;
You could've done:
$HTTP_Request = [System.Net.WebRequest]::Create('http://google.com')
$HTTP_Request.Method = "GET"
$HTTP_Request.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
[System.Net.HttpWebResponse]$HTTP_Response = $HTTP_Request.GetResponse()
Try {
$HTTP_Status = [int]$HTTP_Response.StatusCode
}
Catch {
#handle the error if you like, or not...
}
If ($HTTP_Status -eq 200) {
Write-Host "Good Response"
} Else {
Write-Host "Site Down or Access Denied"
}
# If you got a 404 Not Found, the response will be null so can't be closed
If ($HTTP_Response -eq $null) { } Else { $HTTP_Response.Close() }
You were missing the authentication piece:
$HTTP_Request.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
I re-read your post and because your service account did not have access to the URL you are hitting (which was not actually google.com... you should've put myserver.com...grr), you would never actually get a 200, but always will get an exception. This is a bad practice, because instead of looking for the 200, you would have to always look for the 401 or 407 Unauthorized exception status code specifically, and if the code/response changed to something else, only then is it considered a failure - but technically it always had been a failure, because it never reached the site! It masks potential issues if you intend to go on deliberately using a site your service account doesn't have access to reach. And if your service account was ever granted access, your code would have to be updated.