@@ -26,6 +26,7 @@ public sealed class DefaultSecretManagerProvider : ISecretManagerProvider
26
26
private readonly StartupContextProvider _startupContextProvider ;
27
27
private readonly IAzureStorageProvider _azureStorageProvider ;
28
28
private Lazy < ISecretManager > _secretManagerLazy ;
29
+ private Lazy < bool > _secretsEnabledLazy ;
29
30
30
31
public DefaultSecretManagerProvider ( IOptionsMonitor < ScriptApplicationHostOptions > options , IHostIdProvider hostIdProvider ,
31
32
IConfiguration configuration , IEnvironment environment , ILoggerFactory loggerFactory , IMetricsLogger metricsLogger , HostNameProvider hostNameProvider , StartupContextProvider startupContextProvider , IAzureStorageProvider azureStorageProvider )
@@ -45,74 +46,146 @@ public DefaultSecretManagerProvider(IOptionsMonitor<ScriptApplicationHostOptions
45
46
_loggerFactory = loggerFactory ;
46
47
_metricsLogger = metricsLogger ?? throw new ArgumentNullException ( nameof ( metricsLogger ) ) ;
47
48
_secretManagerLazy = new Lazy < ISecretManager > ( Create ) ;
49
+ _secretsEnabledLazy = new Lazy < bool > ( GetSecretsEnabled ) ;
48
50
49
51
// When these options change (due to specialization), we need to reset the secret manager.
50
52
options . OnChange ( _ => ResetSecretManager ( ) ) ;
51
53
52
54
_azureStorageProvider = azureStorageProvider ;
53
55
}
54
56
57
+ public bool SecretsEnabled
58
+ {
59
+ get
60
+ {
61
+ if ( _secretManagerLazy . IsValueCreated )
62
+ {
63
+ return true ;
64
+ }
65
+ return _secretsEnabledLazy . Value ;
66
+ }
67
+ }
68
+
55
69
public ISecretManager Current => _secretManagerLazy . Value ;
56
70
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
+ }
58
76
59
77
private ISecretManager Create ( ) => new SecretManager ( CreateSecretsRepository ( ) , _loggerFactory . CreateLogger < SecretManager > ( ) , _metricsLogger , _hostNameProvider , _startupContextProvider ) ;
60
78
61
79
internal ISecretsRepository CreateSecretsRepository ( )
62
80
{
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 ) ;
64
135
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
+ {
65
151
string secretStorageType = Environment . GetEnvironmentVariable ( EnvironmentSettingNames . AzureWebJobsSecretStorageType ) ;
66
152
string secretStorageSas = _environment . GetEnvironmentVariable ( EnvironmentSettingNames . AzureWebJobsSecretStorageSas ) ;
67
153
68
154
if ( secretStorageType != null && secretStorageType . Equals ( FileStorage , StringComparison . OrdinalIgnoreCase ) )
69
155
{
70
- repository = new FileSystemSecretsRepository ( _options . CurrentValue . SecretsPath , _loggerFactory . CreateLogger < FileSystemSecretsRepository > ( ) , _environment ) ;
156
+ repositoryType = typeof ( FileSystemSecretsRepository ) ;
157
+ return true ;
71
158
}
72
159
else if ( secretStorageType != null && secretStorageType . Equals ( "keyvault" , StringComparison . OrdinalIgnoreCase ) )
73
160
{
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 ;
82
163
}
83
164
else if ( secretStorageType != null && secretStorageType . Equals ( "kubernetes" , StringComparison . OrdinalIgnoreCase ) )
84
165
{
85
- repository = new KubernetesSecretsRepository ( _environment , new SimpleKubernetesClient ( _environment , _loggerFactory . CreateLogger < SimpleKubernetesClient > ( ) ) ) ;
166
+ repositoryType = typeof ( KubernetesSecretsRepository ) ;
167
+ return true ;
86
168
}
87
169
else if ( secretStorageSas != null )
88
170
{
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 ;
96
173
}
97
174
else if ( _azureStorageProvider . ConnectionExists ( ConnectionStringNames . Storage ) )
98
175
{
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 ;
106
178
}
107
179
else
108
180
{
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 ;
111
183
}
184
+ }
112
185
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 _ ) ;
116
189
}
117
190
}
118
191
}
0 commit comments