@@ -26,6 +26,7 @@ public sealed class DefaultSecretManagerProvider : ISecretManagerProvider
2626 private readonly StartupContextProvider _startupContextProvider ;
2727 private readonly IAzureStorageProvider _azureStorageProvider ;
2828 private Lazy < ISecretManager > _secretManagerLazy ;
29+ private Lazy < bool > _secretsEnabledLazy ;
2930
3031 public DefaultSecretManagerProvider ( IOptionsMonitor < ScriptApplicationHostOptions > options , IHostIdProvider hostIdProvider ,
3132 IConfiguration configuration , IEnvironment environment , ILoggerFactory loggerFactory , IMetricsLogger metricsLogger , HostNameProvider hostNameProvider , StartupContextProvider startupContextProvider , IAzureStorageProvider azureStorageProvider )
@@ -45,74 +46,146 @@ public DefaultSecretManagerProvider(IOptionsMonitor<ScriptApplicationHostOptions
4546 _loggerFactory = loggerFactory ;
4647 _metricsLogger = metricsLogger ?? throw new ArgumentNullException ( nameof ( metricsLogger ) ) ;
4748 _secretManagerLazy = new Lazy < ISecretManager > ( Create ) ;
49+ _secretsEnabledLazy = new Lazy < bool > ( GetSecretsEnabled ) ;
4850
4951 // When these options change (due to specialization), we need to reset the secret manager.
5052 options . OnChange ( _ => ResetSecretManager ( ) ) ;
5153
5254 _azureStorageProvider = azureStorageProvider ;
5355 }
5456
57+ public bool SecretsEnabled
58+ {
59+ get
60+ {
61+ if ( _secretManagerLazy . IsValueCreated )
62+ {
63+ return true ;
64+ }
65+ return _secretsEnabledLazy . Value ;
66+ }
67+ }
68+
5569 public ISecretManager Current => _secretManagerLazy . Value ;
5670
57- private void ResetSecretManager ( ) => Interlocked . Exchange ( ref _secretManagerLazy , new Lazy < ISecretManager > ( Create ) ) ;
71+ private void ResetSecretManager ( )
72+ {
73+ Interlocked . Exchange ( ref _secretsEnabledLazy , new Lazy < bool > ( GetSecretsEnabled ) ) ;
74+ Interlocked . Exchange ( ref _secretManagerLazy , new Lazy < ISecretManager > ( Create ) ) ;
75+ }
5876
5977 private ISecretManager Create ( ) => new SecretManager ( CreateSecretsRepository ( ) , _loggerFactory . CreateLogger < SecretManager > ( ) , _metricsLogger , _hostNameProvider , _startupContextProvider ) ;
6078
6179 internal ISecretsRepository CreateSecretsRepository ( )
6280 {
63- ISecretsRepository repository ;
81+ ISecretsRepository repository = null ;
82+
83+ if ( TryGetSecretsRepositoryType ( out Type repositoryType ) )
84+ {
85+ if ( repositoryType == typeof ( FileSystemSecretsRepository ) )
86+ {
87+ repository = new FileSystemSecretsRepository ( _options . CurrentValue . SecretsPath , _loggerFactory . CreateLogger < FileSystemSecretsRepository > ( ) , _environment ) ;
88+ }
89+ else if ( repositoryType == typeof ( KeyVaultSecretsRepository ) )
90+ {
91+ string azureWebJobsSecretStorageKeyVaultName = Environment . GetEnvironmentVariable ( EnvironmentSettingNames . AzureWebJobsSecretStorageKeyVaultName ) ;
92+ string azureWebJobsSecretStorageKeyVaultConnectionString = Environment . GetEnvironmentVariable ( EnvironmentSettingNames . AzureWebJobsSecretStorageKeyVaultConnectionString ) ;
93+
94+ repository = new KeyVaultSecretsRepository ( Path . Combine ( _options . CurrentValue . SecretsPath , "Sentinels" ) ,
95+ azureWebJobsSecretStorageKeyVaultName ,
96+ azureWebJobsSecretStorageKeyVaultConnectionString ,
97+ _loggerFactory . CreateLogger < KeyVaultSecretsRepository > ( ) ,
98+ _environment ) ;
99+ }
100+ else if ( repositoryType == typeof ( KubernetesSecretsRepository ) )
101+ {
102+ repository = new KubernetesSecretsRepository ( _environment , new SimpleKubernetesClient ( _environment , _loggerFactory . CreateLogger < SimpleKubernetesClient > ( ) ) ) ;
103+ }
104+ else if ( repositoryType == typeof ( BlobStorageSasSecretsRepository ) )
105+ {
106+ string secretStorageSas = _environment . GetEnvironmentVariable ( EnvironmentSettingNames . AzureWebJobsSecretStorageSas ) ;
107+ string siteSlotName = _environment . GetAzureWebsiteUniqueSlotName ( ) ?? _hostIdProvider . GetHostIdAsync ( CancellationToken . None ) . GetAwaiter ( ) . GetResult ( ) ;
108+ repository = new BlobStorageSasSecretsRepository ( Path . Combine ( _options . CurrentValue . SecretsPath , "Sentinels" ) ,
109+ secretStorageSas ,
110+ siteSlotName ,
111+ _loggerFactory . CreateLogger < BlobStorageSasSecretsRepository > ( ) ,
112+ _environment ,
113+ _azureStorageProvider ) ;
114+ }
115+ else if ( repositoryType == typeof ( BlobStorageSecretsRepository ) )
116+ {
117+ string siteSlotName = _environment . GetAzureWebsiteUniqueSlotName ( ) ?? _hostIdProvider . GetHostIdAsync ( CancellationToken . None ) . GetAwaiter ( ) . GetResult ( ) ;
118+ repository = new BlobStorageSecretsRepository ( Path . Combine ( _options . CurrentValue . SecretsPath , "Sentinels" ) ,
119+ ConnectionStringNames . Storage ,
120+ siteSlotName ,
121+ _loggerFactory . CreateLogger < BlobStorageSecretsRepository > ( ) ,
122+ _environment ,
123+ _azureStorageProvider ) ;
124+ }
125+ }
126+
127+ if ( repository == null )
128+ {
129+ throw new InvalidOperationException ( $ "Secret initialization from Blob storage failed due to missing both an Azure Storage connection string and a SAS connection uri. " +
130+ $ "For Blob Storage, please provide at least one of these. If you intend to use files for secrets, add an App Setting key '{ EnvironmentSettingNames . AzureWebJobsSecretStorageType } ' with value '{ FileStorage } '.") ;
131+ }
132+
133+ ILogger logger = _loggerFactory . CreateLogger < DefaultSecretManagerProvider > ( ) ;
134+ logger . LogInformation ( "Resolved secret storage provider {provider}" , repository . Name ) ;
64135
136+ return repository ;
137+ }
138+
139+ /// <summary>
140+ /// Determines the repository Type to use based on configured settings.
141+ /// </summary>
142+ /// <remarks>
143+ /// For scenarios where the app isn't configured for key storage (e.g. no AzureWebJobsSecretStorageType explicitly configured,
144+ /// no storage connection string for default blob storage, etc.). Note that it's still possible for the creation of the repository
145+ /// to fail due to invalid values. This method just does preliminary config checks to determine the Type.
146+ /// </remarks>
147+ /// <param name="repositoryType">The repository Type or null.</param>
148+ /// <returns>True if a Type was determined, false otherwise.</returns>
149+ internal bool TryGetSecretsRepositoryType ( out Type repositoryType )
150+ {
65151 string secretStorageType = Environment . GetEnvironmentVariable ( EnvironmentSettingNames . AzureWebJobsSecretStorageType ) ;
66152 string secretStorageSas = _environment . GetEnvironmentVariable ( EnvironmentSettingNames . AzureWebJobsSecretStorageSas ) ;
67153
68154 if ( secretStorageType != null && secretStorageType . Equals ( FileStorage , StringComparison . OrdinalIgnoreCase ) )
69155 {
70- repository = new FileSystemSecretsRepository ( _options . CurrentValue . SecretsPath , _loggerFactory . CreateLogger < FileSystemSecretsRepository > ( ) , _environment ) ;
156+ repositoryType = typeof ( FileSystemSecretsRepository ) ;
157+ return true ;
71158 }
72159 else if ( secretStorageType != null && secretStorageType . Equals ( "keyvault" , StringComparison . OrdinalIgnoreCase ) )
73160 {
74- string azureWebJobsSecretStorageKeyVaultName = Environment . GetEnvironmentVariable ( EnvironmentSettingNames . AzureWebJobsSecretStorageKeyVaultName ) ;
75- string azureWebJobsSecretStorageKeyVaultConnectionString = Environment . GetEnvironmentVariable ( EnvironmentSettingNames . AzureWebJobsSecretStorageKeyVaultConnectionString ) ;
76-
77- repository = new KeyVaultSecretsRepository ( Path . Combine ( _options . CurrentValue . SecretsPath , "Sentinels" ) ,
78- azureWebJobsSecretStorageKeyVaultName ,
79- azureWebJobsSecretStorageKeyVaultConnectionString ,
80- _loggerFactory . CreateLogger < KeyVaultSecretsRepository > ( ) ,
81- _environment ) ;
161+ repositoryType = typeof ( KeyVaultSecretsRepository ) ;
162+ return true ;
82163 }
83164 else if ( secretStorageType != null && secretStorageType . Equals ( "kubernetes" , StringComparison . OrdinalIgnoreCase ) )
84165 {
85- repository = new KubernetesSecretsRepository ( _environment , new SimpleKubernetesClient ( _environment , _loggerFactory . CreateLogger < SimpleKubernetesClient > ( ) ) ) ;
166+ repositoryType = typeof ( KubernetesSecretsRepository ) ;
167+ return true ;
86168 }
87169 else if ( secretStorageSas != null )
88170 {
89- string siteSlotName = _environment . GetAzureWebsiteUniqueSlotName ( ) ?? _hostIdProvider . GetHostIdAsync ( CancellationToken . None ) . GetAwaiter ( ) . GetResult ( ) ;
90- repository = new BlobStorageSasSecretsRepository ( Path . Combine ( _options . CurrentValue . SecretsPath , "Sentinels" ) ,
91- secretStorageSas ,
92- siteSlotName ,
93- _loggerFactory . CreateLogger < BlobStorageSasSecretsRepository > ( ) ,
94- _environment ,
95- _azureStorageProvider ) ;
171+ repositoryType = typeof ( BlobStorageSasSecretsRepository ) ;
172+ return true ;
96173 }
97174 else if ( _azureStorageProvider . ConnectionExists ( ConnectionStringNames . Storage ) )
98175 {
99- string siteSlotName = _environment . GetAzureWebsiteUniqueSlotName ( ) ?? _hostIdProvider . GetHostIdAsync ( CancellationToken . None ) . GetAwaiter ( ) . GetResult ( ) ;
100- repository = new BlobStorageSecretsRepository ( Path . Combine ( _options . CurrentValue . SecretsPath , "Sentinels" ) ,
101- ConnectionStringNames . Storage ,
102- siteSlotName ,
103- _loggerFactory . CreateLogger < BlobStorageSecretsRepository > ( ) ,
104- _environment ,
105- _azureStorageProvider ) ;
176+ repositoryType = typeof ( BlobStorageSecretsRepository ) ;
177+ return true ;
106178 }
107179 else
108180 {
109- throw new InvalidOperationException ( $ "Secret initialization from Blob storage failed due to missing both an Azure Storage connection string and a SAS connection uri. " +
110- $ "For Blob Storage, please provide at least one of these. If you intend to use files for secrets, add an App Setting key ' { EnvironmentSettingNames . AzureWebJobsSecretStorageType } ' with value ' { FileStorage } '." ) ;
181+ repositoryType = null ;
182+ return false ;
111183 }
184+ }
112185
113- ILogger logger = _loggerFactory . CreateLogger < DefaultSecretManagerProvider > ( ) ;
114- logger . LogInformation ( "Resolved secret storage provider {provider}" , repository . Name ) ;
115- return repository ;
186+ internal bool GetSecretsEnabled ( )
187+ {
188+ return TryGetSecretsRepositoryType ( out _ ) ;
116189 }
117190 }
118191}
0 commit comments