PnP core SDK Context in Azure Function App v4 #893
-
Hello, so far i have this code: var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("local.settings.json", true, true)
.AddEnvironmentVariables()
.Build();
var appConfig = new AzureFunctionSettings();
config.Bind(appConfig);
builder.Services.AddHttpClient();
builder.Services.AddLogging();
builder.Services.AddSingleton<IAzureFunctionSettings>(appConfig);
builder.Services.AddSingleton<IAppAuthProvider, AppAuthProvider>();
builder.Services.AddSingleton<IPnPContextProvider, PnPContextProvider>();
builder.Services.AddSingleton<ISubscriptionsService, SubscriptionsService>(); PnPContextProvider.cs internal class PnPContextProvider : IPnPContextProvider
{
private IAppAuthProvider _authProvider;
private IAzureFunctionSettings _config;
public PnPContextProvider(IAppAuthProvider authProvider, IAzureFunctionSettings config)
{
_authProvider = authProvider;
_config = config;
}
public PnPContext GetContext(string siteUrl)
{
AzureFunctionSettings azureFunctionSettings = null;
var host = new HostBuilder()
.ConfigureServices((context, services) =>
{
// Add our global configuration instance
services.AddSingleton(options =>
{
var configuration = context.Configuration;
azureFunctionSettings = new AzureFunctionSettings();
configuration.Bind(azureFunctionSettings);
return configuration;
});
// Add our configuration class
services.AddSingleton(options => { return _config; });
// Add and configure PnP Core SDK
services.AddPnPCore(options =>
{
// Add the base site url
options.Sites.Add("Default", new PnPCoreSiteOptions
{
SiteUrl = siteUrl
});
});
services.AddPnPCoreAuthentication(options =>
{
// Load the certificate to use
X509Certificate2 cert = LoadCertificate(_config);
// Configure certificate based auth
options.Credentials.Configurations.Add("CertAuth", new PnPCoreAuthenticationCredentialConfigurationOptions
{
ClientId = _config.ClientId,
TenantId = _config.TenantId,
X509Certificate = new PnPCoreAuthenticationX509CertificateOptions
{
Certificate = LoadCertificate(_config),
}
});
// Connect this auth method to the configured site
options.Sites.Add("Default", new PnPCoreAuthenticationSiteOptions
{
AuthenticationProviderName = "CertAuth",
});
});
})
.Build();
// with host.Start(); -> host is started but it gets stucked on line var context = pnpFactory.Create(new Uri(siteUrl));
// with host.Run(); -> it gets stucked directly on line host.Run();
host.Start();
var scope = host.Services.CreateScope();
var conf = scope.ServiceProvider.GetRequiredService<IConfiguration>();
// Obtain a PnP Context factory
var pnpFactory = scope
.ServiceProvider
.GetRequiredService<IPnPContextFactory>();
// Use the PnP Context factory to get a PnPContext for the given configuration
var context = pnpFactory.Create(new Uri(siteUrl));
return context;
}
private static X509Certificate2 LoadCertificate(IAzureFunctionSettings azureFunctionSettings)
{
// Will only be populated correctly when running in the Azure Function host
string certBase64Encoded = Environment.GetEnvironmentVariable("CertificateFromKeyVault");
if (!string.IsNullOrEmpty(certBase64Encoded))
{
// Azure Function flow
return new X509Certificate2(Convert.FromBase64String(certBase64Encoded),
"",
X509KeyStorageFlags.Exportable |
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.EphemeralKeySet);
}
else
{
// Local flow
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
var certificateCollection = store.Certificates.Find(X509FindType.FindByThumbprint, azureFunctionSettings.CertThumbprint, false);
store.Close();
return certificateCollection.First();
}
}
public async Task<HttpClient> GetHttpClientAsync(string siteUrl)
{
string accessToken = await CreateTokenAsync(siteUrl);
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return httpClient;
}
private async Task<string> CreateTokenAsync(string siteUrl)
{
return await _authProvider.CreateTokenForScopesAsync(new string[] { $"{siteUrl.GetSPBaseUrl()}/.default" });
}
}
public interface IPnPContextProvider
{
PnPContext GetContext(string siteUrl);
Task<HttpClient> GetHttpClientAsync(string siteUrl);
} SubscriptionService.cs public class SubscriptionsService : ISubscriptionsService
{
private IAzureFunctionSettings _config;
private IPnPContextProvider _spContextFactory;
public SubscriptionsService(IAzureFunctionSettings config, IPnPContextProvider spAuthProvider)
{
_config = config;
_spContextFactory = spAuthProvider;
}
public async Task EnsureSubscriptionsAsync()
{
var context = _spContextFactory.GetContext(_config.siteURL1);
var context = _spContextFactory.GetContext(_config.siteURL2);
}
}
public interface ISubscriptionsService
{
Task EnsureSubscriptionsAsync();
} also i would like to ask how it is with releasing of resources after azure function call is called? does they release automatically or do i have to call host.Dispose();? Thank you for your help in advance |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
@Nevinia210 : why don't you use the OOB provided Regarding dispose, when you're using a using statement the |
Beta Was this translation helpful? Give feedback.
-
@jansenbe : so configuration for that contextFactory on top of multiple possible webs during the run would then look like that? // Add and configure PnP Core SDK
services.AddPnPCore(options =>
{
// Add all possible base site urls
listOfUrls.ForEach(url =>
{
options.Sites.Add(url, new PnPCoreSiteOptions
{
SiteUrl = url
});
});
}); |
Beta Was this translation helpful? Give feedback.
@Nevinia210 : why don't you use the OOB provided
IPnPContextFactory
as shown here (https://github.com/pnp/pnpcore/blob/dev/samples/Demo.AzureFunction.OutOfProcess.AppOnly/CreateSite.cs#L23) for using it and here for configuring it: https://github.com/pnp/pnpcore/blob/dev/samples/Demo.AzureFunction.OutOfProcess.AppOnly/Program.cs#L35-L64. Once you've done that you can create aPnPContext
for the passed in URL viausing (var pnpContext = await contextFactory.CreateAsync(new Uri("https://contoso.sharepoint.com/sites/asite")))
Regarding dispose, when you're using a using statement the
PnPContext
is disposed automatically, but for the moment there's nothing to dispose, this was build with futu…