I am using Powershell and the Exchange Web Service (v1.2) to send an email through Office 365. As long as I use my credentials the script is able to send without an issue. However, for tracking purposes, on script errors I need to send the email from a shared mailbox instead of through my account. The error I get is:
Exception calling "AutodiscoverUrl" with "2" argument(s): "The Autodiscover service couldn't be located."
At F:\Scripting\1-Dev\Modules\Include.ps1:387 char:31
+ $service.AutodiscoverUrl <<<< ($Credential.UserName, {$true})
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
I'm sure the problem is authentication. How can I authenticate and send from a shared mailbox in o365?
EDIT:
Here is the code that I'm using to send the email:
Function Send-O365Email {
[CmdletBinding()]
param(
[Parameter(Position=1, Mandatory=$true)]
[String[]] $To,
[Parameter(Position=2, Mandatory=$true)]
[String] $Subject,
[Parameter(Position=3, Mandatory=$true)]
[String] $Body,
[Parameter(Position=4, Mandatory=$true)]
[System.Management.Automation.PSCredential] $Credential,
[Parameter(Mandatory=$false)]
[String]$PathToAttachment
)
begin {
#Load the EWS Managed API Assembly
Add-Type -Path 'C:\Program Files\Microsoft\Exchange\Web Services\1.2\Microsoft.Exchange.WebServices.dll'
}
process {
#Create the EWS service object
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList Exchange2010_SP1
#Set the credentials for Exchange Online
$service.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList `
$Credential.UserName, $Credential.GetNetworkCredential().Password
#Determine the EWS endpoint using autodiscover
$service.AutodiscoverUrl($Credential.UserName, {$true})
#Create the email message and set the Subject and Body
$message = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage -ArgumentList $service
$message.Subject = $Subject
$message.Body = $Body
$message.Body.BodyType = 'HTML'
if (Test-Path $Attachment) {
$message.Attachments.AddFileAttachment("$Attachment");
}
#Add each specified recipient
$To | ForEach-Object {
$null = $message.ToRecipients.Add($_)
}
#Send the message and save a copy in the users Sent Items folder
try {
$message.SendAndSaveCopy()
}
catch [exception] {
Add-Content -Path $LOGFILE -Value "$_"
}
} # End Process
}
You may want to try the 2.0 assembly, though I don't think that is the issue http://www.microsoft.com/en-us/download/details.aspx?id=35371
My environment is a little different, in that our credentials and email address are not the same name. (one is initialLastName#Comp.com and the other is first.Last#Comp.com) so I have to be a little different in how I approach names, always needing a Credential and a MailBox name.
When I try to use a Resource account, I have to use my email for the autodiscovery. Possibly because the Service account doesn't have a user license. I am speculating.
I found a good amount of resources at http://gsexdev.blogspot.com/.
Specifically, this may be what you need:
## Optional section for Exchange Impersonation
# http://msdn.microsoft.com/en-us/library/exchange/dd633680(v=exchg.80).aspx
$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, "SupportResource#comp.com")
#or clear with
#$service.ImpersonatedUserId = $null
similar 'discussion' # EWS Managed API: how to set From of email? also.
Related
We have a requirement to create office 365 user with mail account through create_user graph api. But user has been created successfully but mail account did not created.
Let us know api name to create user with mail account directly through api.
Microsoft Graph API doesn't has the feature. For mailbox creation, you need to use Exchange PowerShell to do it using New-Mailbox cmdlet. You can automate the Exchange PowerShell with .Net as well :)
For example,
$password = Read-Host "Enter password" -AsSecureString
New-Mailbox -UserPrincipalName chris#contoso.com -Alias chris -Database "Mailbox Database 1" -Name ChrisAshton -OrganizationalUnit Users -Password $password -FirstName Chris -LastName Ashton -DisplayName "Chris Ashton" -ResetPasswordOnNextLogon $true
To do this through the graph API you need to have a license that has Exchange Online as a service plan like Microsoft 365 F3 (SPE_F1), assign your new user one these licenses on creation and a Mailbox will be created for them:
Post the following:
{
"addLicenses": [
{
"skuId": "your SPE_F1 skuId number"
}
],
"removeLicenses": []
}
to:
https://graph.microsoft.com/v1.0/users/{user - id}/assignLicense/
I have a Jenkins server running on Windows. It stores a username:password in the credentials plugin. This is a service user that gets its password updated regularly.
I'm looking for a way to run a script, preferably Powershell, that will update that credential in the Jenkins password store so that it's always up to date when I use it in a build job script.
The password is managed by a Thycotic Secret Server install so I should be able to automate the process of keeping this password up to date, but I have found almost no leads for how to accomplish this, even though the blog post by the guy who wrote the credentials api mentions almost exactly this scenario and then proceeds to just link to the credentials plugin's download page that says nothing about how to actually use the api.
Update
The accepted answer works perfectly, but the rest method call example is using curl, which if you're using windows doesn't help much. Especially if you are trying to invoke the REST URL but your Jenkins server is using AD Integration. To achieve this you can use the following script.
Find the userId and API Token by going to People > User > configure > Show API Token.
$user = "UserID"
$pass = "APIToken"
$pair = "${user}:${pass}"
$bytes = [System.Text.Encoding]::ASCII.GetBytes($pair)
$base64 = [System.Convert]::ToBase64String($bytes)
$basicAuthValue = "Basic $base64"
$headers = #{ Authorization = $basicAuthValue }
Invoke-WebRequest `
-uri "http://YourJenkinsServer:8080/scriptler/run/changeCredentialPassword.groovy?username=UrlEncodedTargetusername&password=URLEncodedNewPassword" `
-method Get `
-Headers $headers
Jenkins supports scripting with the Groovy language. You can get a scripting console by opening in a browser the URL /script of your Jenkins instance. (i.e: http://localhost:8080/script)
The advantage of the Groovy language (over powershell, or anything else) is that those Groovy scripts are executed within Jenkins and have access to everything (config, plugins, jobs, etc).
Then the following code would change the password for user 'BillHurt' to 's3crEt!':
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl
def changePassword = { username, new_password ->
def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
com.cloudbees.plugins.credentials.common.StandardUsernameCredentials.class,
Jenkins.instance
)
def c = creds.findResult { it.username == username ? it : null }
if ( c ) {
println "found credential ${c.id} for username ${c.username}"
def credentials_store = Jenkins.instance.getExtensionList(
'com.cloudbees.plugins.credentials.SystemCredentialsProvider'
)[0].getStore()
def result = credentials_store.updateCredentials(
com.cloudbees.plugins.credentials.domains.Domain.global(),
c,
new UsernamePasswordCredentialsImpl(c.scope, c.id, c.description, c.username, new_password)
)
if (result) {
println "password changed for ${username}"
} else {
println "failed to change password for ${username}"
}
} else {
println "could not find credential for ${username}"
}
}
changePassword('BillHurt', 's3crEt!')
Classic automation (/scriptText)
To automate the execution of this script, you can save it to a file (let's say /tmp/changepassword.groovy) and run the following curl command:
curl -d "script=$(cat /tmp/changepassword.groovy)" http://localhost:8080/scriptText
which should respond with a HTTP 200 status and text:
found credential 801cf176-3455-4b6d-a461-457a288fd202 for username BillHurt
password changed for BillHurt
Automation with the Scriptler plugin
You can also install the Jenkins Scriptler plugin and proceed as follow:
Open the Scriptler tool in side menu
fill up the 3 first field taking care to set the Id field to changeCredentialPassword.groovy
check the Define script parameters checkbox
add 2 parameters: username and password
paste the following script:
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl
def changePassword = { username, new_password ->
def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
com.cloudbees.plugins.credentials.common.StandardUsernameCredentials.class,
jenkins.model.Jenkins.instance
)
def c = creds.findResult { it.username == username ? it : null }
if ( c ) {
println "found credential ${c.id} for username ${c.username}"
def credentials_store = jenkins.model.Jenkins.instance.getExtensionList(
'com.cloudbees.plugins.credentials.SystemCredentialsProvider'
)[0].getStore()
def result = credentials_store.updateCredentials(
com.cloudbees.plugins.credentials.domains.Domain.global(),
c,
new UsernamePasswordCredentialsImpl(c.scope, null, c.description, c.username, new_password)
)
if (result) {
println "password changed for ${username}"
} else {
println "failed to change password for ${username}"
}
} else {
println "could not find credential for ${username}"
}
}
changePassword("$username", "$password")
and click the Submit button
Now you can call the following URL to change the password (replacing the username and password parameter): http://localhost:8080/scriptler/run/changeCredentialPassword.groovy?username=BillHurt&password=s3crEt%21 (notice the need to urlencode the parameters' value)
or with curl:
curl -G http://localhost:8080/scriptler/run/changeCredentialPassword.groovy --data-urlencode 'username=BillHurt' --data-urlencode "password=s3crEt!"
sources:
Printing a list of credentials and their IDs
Create UserPrivateKeySource Credential via Groovy?
credential plugin source code
Scriptler plugin
Search engine tip: use keywords 'Jenkins.instance.', 'com.cloudbees.plugins.credentials' and UsernamePasswordCredentialsImpl
Decided to write a new answer although it is basically some update to #Tomasleveil's answer:
removing deprecated calls (thanks to jenkins wiki, for other listing options see plugin consumer guide)
adding some comments
preserve credentials ID to avoid breaking existing jobs
lookup credentials by description because usernames are rarely so unique (reader can easily change this to ID lookup)
Here it goes:
credentialsDescription = "my credentials description"
newPassword = "hello"
// list credentials
def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
// com.cloudbees.plugins.credentials.common.StandardUsernameCredentials to catch all types
com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials.class,
Jenkins.instance,
null,
null
);
// select based on description (based on ID might be even better)
cred = creds.find { it.description == credentialsDescription}
println "current values: ${cred.username}:${cred.password} / ${cred.id}"
// not sure what the other stores would be useful for, but you can list more stores by
// com.cloudbees.plugins.credentials.CredentialsProvider.all()
credentials_store = jenkins.model.Jenkins.instance.getExtensionList(
'com.cloudbees.plugins.credentials.SystemCredentialsProvider'
)[0].getStore()
// replace existing credentials with a new instance
updated = credentials_store.updateCredentials(
com.cloudbees.plugins.credentials.domains.Domain.global(),
cred,
// make sure you create an instance from the correct type
new com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl(cred.scope, cred.id, cred.description, cred.username, newPassword)
)
if (updated) {
println "password changed for '${cred.description}'"
} else {
println "failed to change password for '${cred.description}'"
}
I never found a way to get the Groovy script to stop updating the credential ID, but I noticed that if I used the web interface to update credentials, the ID did not change.
With that in mind the script below will in effect script the Jenkins web interface to do the updates.
Just for clarification, the reason this is important is because if you are using something like the Credentials binding plugin to use credentials in your job, updating the ID to the credential will break that link and your job will fail. Please excuse the use of $args rather than param() as this is just an ugly hack that I will refine later.
Note the addition of the json payload to the form fields. I found out the hard way that this is required in this very specific format or the form submission will fail.
$username = $args[0]
$password = $args[1]
Add-Type -AssemblyName System.Web
#1. Log in and capture the session.
$homepageResponse = Invoke-WebRequest -uri http://Servername.domain.com/login?from=%2F -SessionVariable session
$loginForm = $homepageResponse.Forms['login']
$loginForm.Fields.j_username = $username
$loginForm.Fields.j_password = $password
$loginResponse = Invoke-WebRequest `
-Uri http://Servername.domain.com/j_acegi_security_check `
-Method Post `
-Body $loginForm.Fields `
-WebSession $session
#2. Get Credential ID
$uri = "http://Servername.domain.com/credential-store/domain/_/api/xml"
foreach($id in [string]((([xml](Invoke-WebRequest -uri $uri -method Get -Headers $headers -WebSession $session).content)).domainWrapper.Credentials | Get-Member -MemberType Property).Name -split ' '){
$id = $id -replace '_',''
$uri = "http://Servername.domain.com/credential-store/domain/_/credential/$id/api/xml"
$displayName = ([xml](Invoke-WebRequest -uri $uri -method Get -Headers $headers -WebSession $session).content).credentialsWrapper.displayName
if($displayName -match $username){
$credentialID = $id
}
}
#3. Get Update Form
$updatePage = Invoke-WebRequest -Uri "http://Servername.domain.com/credential-store/domain/_/credential/$credentialID/update" -WebSession $session
$updateForm = $updatePage.Forms['update']
$updateForm.Fields.'_.password' = $password
#4. Submit Update Form
$json = #{"stapler-class" = "com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl";
"scope"="GLOBAL";"username"="domain\$username";"password"=$password;"description"="";"id"=$id} | ConvertTo-Json
$updateForm.Fields.Add("json",$json)
Invoke-WebRequest `
-Uri "http://Servername.domain.com/credential-store/domain/_/credential/$credentialID/updateSubmit" `
-Method Post `
-Body $updateForm.Fields `
-WebSession $session
I want to redeploy a multiple tenants using powershell. We have 100+ tenants in UIT and I don't want to do this manually. In order to redeploy, I have to use a specific link and log in. My question is, if it is possible to do this in an automated way using powershell. Here is what I have so far:
Function SendRequest($tenantName)
{
$url = "http://<machine-name>/$tenantName/Account /LogOn?ReturnUrl=%2f$tenantName%2fadmin%2fdeploytenant%3fsyncmetadata%3dtrue&syncmetadata=true"
$req = [system.Net.WebRequest]::Create($url)
$req.Credentials = [System.Net.NetworkCredential]::($username, (ConvertTo-SecureString $pwd -AsPlainText -force))
try
{
$res = $req.GetResponse()
}
catch [System.Net.WebException]
{
$res = $_.Exception.Response
}
$int = [int]$res.StatusCode
$status = $res.StatusCode
Write-Host $status
return "$int $status"
}
The status that is returned is "200 OK" but it does not redeploy. Maybe I need to log in first and then send another request with the redeployment parameters. So what would be the best way to accomplish this?
Maybe this is just a snippet of your code, but I'm seeing you reference $username and $pwd without actually feeding in a username and password.
Perhaps that could that explain the lack of a successful redeploy.
Does anyone know of a PowerShell script that uses EWS to look into a user's Exchange 2010 SP3 Inbox and get a count of email messages and the sizes older than 60 days for example? I can't believe no one has been able to do this. I see a lot of people asking how to do this. Help please :-)
There are a few example on this http://gsexdev.blogspot.com.au/2012/10/reporting-on-item-age-count-and-size-in.html and http://gsexdev.blogspot.com.au/2014/07/creating-mailbox-folder-growth-map-with.html
Specifically for what you want to do something like this should work
## Get the Mailbox to Access from the 1st commandline argument
$MailboxName = $args[0]
$DateFrom = (Get-Date).AddDays(-60)
## Load Managed API dll
Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.1\Microsoft.Exchange.WebServices.dll"
## Set Exchange Version
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2
## Create Exchange Service Object
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials
#Credentials Option 1 using UPN for the windows Account
$psCred = Get-Credential
$creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())
$service.Credentials = $creds
#Credentials Option 2
#service.UseDefaultCredentials = $true
## Choose to ignore any SSL Warning issues caused by Self Signed Certificates
## Code From http://poshcode.org/624
## Create a compilation environment
$Provider=New-Object Microsoft.CSharp.CSharpCodeProvider
$Compiler=$Provider.CreateCompiler()
$Params=New-Object System.CodeDom.Compiler.CompilerParameters
$Params.GenerateExecutable=$False
$Params.GenerateInMemory=$True
$Params.IncludeDebugInformation=$False
$Params.ReferencedAssemblies.Add("System.DLL") | Out-Null
$TASource=#'
namespace Local.ToolkitExtensions.Net.CertificatePolicy{
public class TrustAll : System.Net.ICertificatePolicy {
public TrustAll() {
}
public bool CheckValidationResult(System.Net.ServicePoint sp,
System.Security.Cryptography.X509Certificates.X509Certificate cert,
System.Net.WebRequest req, int problem) {
return true;
}
}
}
'#
$TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
$TAAssembly=$TAResults.CompiledAssembly
## We now create an instance of the TrustAll and attach it to the ServicePointManager
$TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
[System.Net.ServicePointManager]::CertificatePolicy=$TrustAll
## end code from http://poshcode.org/624
## Set the URL of the CAS (Client Access Server) to use two options are availbe to use Autodiscover to find the CAS URL or Hardcode the CAS to use
#CAS URL Option 1 Autodiscover
$service.AutodiscoverUrl($MailboxName,{$true})
"Using CAS Server : " + $Service.url
#CAS URL Option 2 Hardcoded
#$uri=[system.URI] "https://casservername/ews/exchange.asmx"
#$service.Url = $uri
## Optional section for Exchange Impersonation
#$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)
# Bind to the Inbox Folder
$folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName)
$Inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
#Define ItemView to retrive just 1000 Items
$ivItemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(250)
$psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly)
$psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::Size)
$psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived)
$psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeCreated)
$ivItemView.PropertySet = $psPropset
$sfItemSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThan([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived,$DateFrom)
$rptObj = "" | Select Mailbox,TotalNumber,TotalSize
$rptObj.Mailbox = $MailboxName
$rptObj.TotalNumber = 0
$rptObj.TotalSize = [Int64]0
$fiItems = $null
do{
$fiItems = $service.FindItems($Inbox.Id,$sfItemSearchFilter,$ivItemView)
#[Void]$service.LoadPropertiesForItems($fiItems,$psPropset)
foreach($Item in $fiItems.Items){
$rptObj.TotalNumber += 1
$rptObj.TotalSize += [Int64]$Item.Size
}
$ivItemView.Offset += $fiItems.Items.Count
}while($fiItems.MoreAvailable -eq $true)
$rptObj | select Mailbox,TotalNumber,#{label="TotalSize(MB)";expression={[math]::Round($_.TotalSize/1MB,2)}}
I use the following code to set some sessions when an user logs in using his twitter account:
$twitteroauth = new TwitterOAuth(YOUR_CONSUMER_KEY, YOUR_CONSUMER_SECRET, $_SESSION['oauth_token'], $_SESSION['oauth_token_secret']);
$access_token = $twitteroauth->getAccessToken($_GET['oauth_verifier']);
$_SESSION['access_token'] = $access_token;
$user_info = $twitteroauth->get('account/verify_credentials');
As you see, I have all the tokens (token, secret token and access token) stored in a session, so that I can use this later on when an user wants to change his profile picture for example
But when I want to have access again to the user info... I am not able to access it. Again I build the twitteroauth connection, but now using the sessions stored during login:
$twitteroauth = new TwitterOAuth(YOUR_CONSUMER_KEY, YOUR_CONSUMER_SECRET, $_SESSION['oauth_token'], $_SESSION['oauth_token_secret']);
$access_token = $_SESSION['access_token'];
$user_info = $twitteroauth->get('account/verify_credentials');
if (isset($user_info->error)) {
echo "token:", $_SESSION['oauth_token'], "<br>";
echo "token_secret:", $_SESSION['oauth_token_secret'], "<br>";
echo "access-token:", $access_token, "<br>";
echo "error";
} else {
echo "oke";
}
I receive the error echo. When I echo my token strings, the all contain data and the access-token contains the value "Array".
Does someone know what I am doing wrong?
You need to use print_r to print the array values.