From aa728eed6d171cb58ed5c9a671b70a3cfbc4a349 Mon Sep 17 00:00:00 2001 From: Ahson Khan Date: Wed, 25 Sep 2024 16:56:57 -0700 Subject: [PATCH] Use code snippets from managed identity credential samples in identity readme doc and fix minor generation issues (#6020) * Use code snippets from managed identity credential samples in identity readme doc. * Generate readme from snippets. * Update snippet generation script to remove unnecessary indentation and extra new line at eof. * Update Snippet Generation doc to show a concrete example on how to run it. * Update other repo READMEs with the generation fixes. * Fix KeyVault Secrets sample and use the snippets in its README * Use the added sample snippet. --- doc/SnippetGeneration.md | 7 ++- eng/scripts/Generate-Snippets.ps1 | 27 ++++++++- sdk/core/azure-core-amqp/README.md | 33 +++++----- sdk/core/azure-core-amqp/samples/README.md | 27 ++++----- sdk/identity/azure-identity/README.md | 15 +++-- .../azure-identity/samples/CMakeLists.txt | 2 +- .../samples/managed_identity_credential.cpp | 60 +++++++++++++++++++ .../azure-security-keyvault-secrets/README.md | 19 ++++-- .../sample1_basic_operations.cpp | 2 +- 9 files changed, 143 insertions(+), 49 deletions(-) diff --git a/doc/SnippetGeneration.md b/doc/SnippetGeneration.md index d9c1a5828..87d1a9f26 100644 --- a/doc/SnippetGeneration.md +++ b/doc/SnippetGeneration.md @@ -110,9 +110,14 @@ The snippet generation tool, located in `eng\scripts\Generate-Snippets.ps1`, is ```bash pwsh -f eng\scripts\Generate-Snippets.ps1 -source_dir -output_dir - ``` +For example, you can run the snippet generation tool for all the packages in the repo, as follows: + +```bash +pwsh -f .\eng\scripts\Generate-Snippets.ps1 -source_dir sdk -output_dir sdk + ``` + The tool will recursively search the source directory for snippet sources and parse the snippet sources in each source file. diff --git a/eng/scripts/Generate-Snippets.ps1 b/eng/scripts/Generate-Snippets.ps1 index 1469d87f1..88e24d343 100644 --- a/eng/scripts/Generate-Snippets.ps1 +++ b/eng/scripts/Generate-Snippets.ps1 @@ -82,7 +82,28 @@ function ProcessSnippetsInFile { # Insert the snippet text. $snippet_text = $snippet_map[$snippet_name] - $output_file_contents = $output_file_contents -replace "\s+", "`r`n``````cpp`r`n$snippet_text`r`n```````r`n`r`n" + + # Remove leading spaces from each line, by first splitting the text into lines. + $lines = $snippet_text -split [Environment]::NewLine + + # Then, find the minimum leading whitespace across all lines. + # This is done to trim the minimum amount of leading whitespace from each line while preserving the relative indentation for lines that are already indented. + # When calculating the min whitespace, we only consider lines that are not empty. + $minWhitespace = ($lines | Where-Object { $_.Trim() -ne '' } | ForEach-Object { $_.IndexOf($_.TrimStart()) }) | Measure-Object -Minimum | Select-Object -ExpandProperty Minimum + + # Trim the minimum whitespace from each line + $trimmedLines = $lines | ForEach-Object { + if ($_.Length -gt $minWhitespace) { + $_.Substring($minWhitespace) + } else { + $_ + } + } + + # Join the lines back into a single string + $snippet_text_clean = $trimmedLines -join [Environment]::NewLine + + $output_file_contents = $output_file_contents -replace "\s+", "`r`n``````cpp`r`n$snippet_text_clean`r`n```````r`n`r`n" } elseif ($output_file.Extension -eq '.hpp') { @@ -98,8 +119,8 @@ function ProcessSnippetsInFile { } # The Regex::Replace above inserts an extra newline at the end of the file. Remove it. - $output_file_contents = $output_file_contents -replace "`r`n\s*\Z", "`r`n" - $original_contents = $original_file_contents -replace "`r`n\s*\Z", "`r`n" + $output_file_contents = $output_file_contents -replace "`r`n\s*\Z", "" + $original_contents = $original_file_contents -replace "`r`n\s*\Z", "" if ($verify) { if ($output_file_contents -ne $original_contents) { diff --git a/sdk/core/azure-core-amqp/README.md b/sdk/core/azure-core-amqp/README.md index 65fc53d00..cb671295e 100644 --- a/sdk/core/azure-core-amqp/README.md +++ b/sdk/core/azure-core-amqp/README.md @@ -45,31 +45,31 @@ An AMQP Message Sender is responsible for sending messages to an AMQP server ove ```cpp - Azure::Core::Amqp::_internal::MessageSenderOptions senderOptions; - senderOptions.Name = "sender-link"; - senderOptions.MessageSource = "source"; - senderOptions.SettleMode = Azure::Core::Amqp::_internal::SenderSettleMode::Unsettled; - senderOptions.MaxMessageSize = (std::numeric_limits::max)(); +Azure::Core::Amqp::_internal::MessageSenderOptions senderOptions; +senderOptions.Name = "sender-link"; +senderOptions.MessageSource = "source"; +senderOptions.SettleMode = Azure::Core::Amqp::_internal::SenderSettleMode::Unsettled; +senderOptions.MaxMessageSize = (std::numeric_limits::max)(); - Azure::Core::Amqp::_internal::MessageSender sender( - session, credentials->GetEntityPath(), senderOptions, nullptr); +Azure::Core::Amqp::_internal::MessageSender sender( + session, credentials->GetEntityPath(), senderOptions, nullptr); ``` Once the message sender has been created, it can be used to send messages to the remote server. ```cpp - Azure::Core::Amqp::Models::AmqpMessage message; - message.SetBody(Azure::Core::Amqp::Models::AmqpBinaryData{'H', 'e', 'l', 'l', 'o'}); +Azure::Core::Amqp::Models::AmqpMessage message; +message.SetBody(Azure::Core::Amqp::Models::AmqpBinaryData{'H', 'e', 'l', 'l', 'o'}); - constexpr int maxMessageSendCount = 5; +constexpr int maxMessageSendCount = 5; - int messageSendCount = 0; - while (messageSendCount < maxMessageSendCount) - { - auto result = sender.Send(message); - messageSendCount += 1; - } +int messageSendCount = 0; +while (messageSendCount < maxMessageSendCount) +{ + auto result = sender.Send(message); + messageSendCount += 1; +} ``` ## Next steps @@ -106,4 +106,3 @@ Azure SDK for C++ is licensed under the [MIT](https://github.com/Azure/azure-sdk [cloud_shell_bash]: https://shell.azure.com/bash ![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-cpp%2Fsdk%2Fcore%2Fcore-opentelemetry%2FREADME.png) - diff --git a/sdk/core/azure-core-amqp/samples/README.md b/sdk/core/azure-core-amqp/samples/README.md index 11d8dd533..060994ed6 100644 --- a/sdk/core/azure-core-amqp/samples/README.md +++ b/sdk/core/azure-core-amqp/samples/README.md @@ -75,24 +75,24 @@ Demonstrates writing messages to the Azure Event Hubs service using the AMQP pro ```cpp - Azure::Core::Amqp::_internal::MessageSenderOptions senderOptions; - senderOptions.Name = "sender-link"; - senderOptions.MessageSource = "source"; - senderOptions.SettleMode = Azure::Core::Amqp::_internal::SenderSettleMode::Unsettled; - senderOptions.MaxMessageSize = (std::numeric_limits::max)(); +Azure::Core::Amqp::_internal::MessageSenderOptions senderOptions; +senderOptions.Name = "sender-link"; +senderOptions.MessageSource = "source"; +senderOptions.SettleMode = Azure::Core::Amqp::_internal::SenderSettleMode::Unsettled; +senderOptions.MaxMessageSize = (std::numeric_limits::max)(); - Azure::Core::Amqp::_internal::MessageSender sender( - session, credentials->GetEntityPath(), senderOptions, nullptr); +Azure::Core::Amqp::_internal::MessageSender sender( + session, credentials->GetEntityPath(), senderOptions, nullptr); ``` ```cpp - Azure::Core::Amqp::_internal::ConnectionOptions connectionOptions; - connectionOptions.ContainerId = "whatever"; - connectionOptions.EnableTrace = true; - connectionOptions.Port = credential->GetPort(); - Azure::Core::Amqp::_internal::Connection connection( - credential->GetHostName(), credential, connectionOptions); +Azure::Core::Amqp::_internal::ConnectionOptions connectionOptions; +connectionOptions.ContainerId = "whatever"; +connectionOptions.EnableTrace = true; +connectionOptions.Port = credential->GetPort(); +Azure::Core::Amqp::_internal::Connection connection( + credential->GetHostName(), credential, connectionOptions); ``` ### eventhub_token_reader_sample @@ -112,4 +112,3 @@ Demonstrates receiving messages from a local AMQP server using the AMQP protocol ### eventhub_get_eventhub_properties_sample Demonstrates receiving messages from the Azure Event Hubs service using an AMQP Management API. - diff --git a/sdk/identity/azure-identity/README.md b/sdk/identity/azure-identity/README.md index aebf9b838..4e7204db8 100644 --- a/sdk/identity/azure-identity/README.md +++ b/sdk/identity/azure-identity/README.md @@ -104,16 +104,16 @@ Many Azure hosts allow the assignment of a user-assigned managed identity. The f To use a client ID, create a `ManagedIdentityId` with `ManagedIdentityIdKind::ClientId`, set that as the `IdentityId` in the `ManagedIdentityCredentialOptions`, and pass that to the `ManagedIdentityCredential` constructor. For example: + ```cpp -// When deployed to an Azure host, ManagedIdentityCredential will authenticate the specified user-assigned managed identity. +// When deployed to an Azure host, ManagedIdentityCredential will authenticate the specified +// user-assigned managed identity. std::string userAssignedClientId = ""; ManagedIdentityCredentialOptions options; options.IdentityId = ManagedIdentityId(ManagedIdentityIdKind::ClientId, userAssignedClientId); auto credential = std::make_shared(options); - -std::string blobUrl = "https://myaccount.blob.core.windows.net/mycontainer/myblob"; auto blobClient = BlobClient(blobUrl, credential); ``` @@ -121,13 +121,14 @@ auto blobClient = BlobClient(blobUrl, credential); Similarly, to use a resource ID, create a `ManagedIdentityId` with `ManagedIdentityIdKind::ResourceId`, set that as the `IdentityId` in the `ManagedIdentityCredentialOptions`, and pass that to the `ManagedIdentityCredential` constructor. The resource ID takes the form `/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}`. Because resource IDs can be built by convention, they can be more convenient when there are a large number of user-assigned managed identities in your environment. For example: + ```cpp std::string userAssignedResourceId = ""; ManagedIdentityCredentialOptions options; -options.IdentityId = ManagedIdentityId(ManagedIdentityIdKind::ResourceId, userAssignedResourceId); +options.IdentityId + = ManagedIdentityId(ManagedIdentityIdKind::ResourceId, userAssignedResourceId); auto credential = std::make_shared(options); - auto blobClient = BlobClient(blobUrl, credential); ``` @@ -135,13 +136,13 @@ auto blobClient = BlobClient(blobUrl, credential); Similarly, to use an object ID, create a `ManagedIdentityId` with `ManagedIdentityIdKind::ObjectId`, set that as the `IdentityId` in the `ManagedIdentityCredentialOptions`, and pass that to the `ManagedIdentityCredential` constructor. For example: + ```cpp std::string userAssignedObjectId = ""; ManagedIdentityCredentialOptions options; options.IdentityId = ManagedIdentityId(ManagedIdentityIdKind::ObjectId, userAssignedObjectId); auto credential = std::make_shared(options); - auto blobClient = BlobClient(blobUrl, credential); ``` @@ -149,6 +150,7 @@ auto blobClient = BlobClient(blobUrl, credential); You can express your intent to use a system-assigned managed identity, explicitly, by creating a `ManagedIdentityId` with `ManagedIdentityIdKind::SystemAssigned` and an empty ID value, set that as the `IdentityId` in the `ManagedIdentityCredentialOptions`, and pass that to the `ManagedIdentityCredential` constructor. For example: + ```cpp ManagedIdentityCredentialOptions options; options.IdentityId = ManagedIdentityId(ManagedIdentityIdKind::SystemAssigned, {}); @@ -159,6 +161,7 @@ auto blobClient = BlobClient(blobUrl, credential); An alternative way to specify a system-assigned managed identity, implicitly, is by calling the default `ManagedIdentityCredential` constructor. For example: + ```cpp auto credential = std::make_shared(); auto blobClient = BlobClient(blobUrl, credential); diff --git a/sdk/identity/azure-identity/samples/CMakeLists.txt b/sdk/identity/azure-identity/samples/CMakeLists.txt index 26214b043..f8eb917b9 100644 --- a/sdk/identity/azure-identity/samples/CMakeLists.txt +++ b/sdk/identity/azure-identity/samples/CMakeLists.txt @@ -54,7 +54,7 @@ target_compile_definitions(environment_credential_sample PRIVATE _azure_BUILDING create_per_service_target_build_for_sample(identity environment_credential_sample) add_executable(managed_identity_credential_sample managed_identity_credential.cpp) -target_link_libraries(managed_identity_credential_sample PRIVATE azure-identity service) +target_link_libraries(managed_identity_credential_sample PRIVATE azure-identity service azure-storage-blobs) target_include_directories(managed_identity_credential_sample PRIVATE .) target_compile_definitions(managed_identity_credential_sample PRIVATE _azure_BUILDING_SAMPLES) create_per_service_target_build_for_sample(identity managed_identity_credential_sample) diff --git a/sdk/identity/azure-identity/samples/managed_identity_credential.cpp b/sdk/identity/azure-identity/samples/managed_identity_credential.cpp index 1c50eca10..59f4a8ba8 100644 --- a/sdk/identity/azure-identity/samples/managed_identity_credential.cpp +++ b/sdk/identity/azure-identity/samples/managed_identity_credential.cpp @@ -3,9 +3,67 @@ #include #include +#include #include +static void ShowDifferentManagedIdentityApproaches() +{ + using namespace Azure::Identity; + using namespace Azure::Storage::Blobs; + + std::string blobUrl = "https://myaccount.blob.core.windows.net/mycontainer/myblob"; + { + // @begin_snippet: UserAssignedManagedIdentityViaClientId + // When deployed to an Azure host, ManagedIdentityCredential will authenticate the specified + // user-assigned managed identity. + + std::string userAssignedClientId = ""; + ManagedIdentityCredentialOptions options; + options.IdentityId = ManagedIdentityId(ManagedIdentityIdKind::ClientId, userAssignedClientId); + + auto credential = std::make_shared(options); + auto blobClient = BlobClient(blobUrl, credential); + // @end_snippet + } + { + // @begin_snippet: UserAssignedManagedIdentityViaResourceId + std::string userAssignedResourceId = ""; + ManagedIdentityCredentialOptions options; + options.IdentityId + = ManagedIdentityId(ManagedIdentityIdKind::ResourceId, userAssignedResourceId); + + auto credential = std::make_shared(options); + auto blobClient = BlobClient(blobUrl, credential); + // @end_snippet + } + { + // @begin_snippet: UserAssignedManagedIdentityViaObjectId + std::string userAssignedObjectId = ""; + ManagedIdentityCredentialOptions options; + options.IdentityId = ManagedIdentityId(ManagedIdentityIdKind::ObjectId, userAssignedObjectId); + + auto credential = std::make_shared(options); + auto blobClient = BlobClient(blobUrl, credential); + // @end_snippet + } + { + // @begin_snippet: SystemAssignedManagedIdentity + ManagedIdentityCredentialOptions options; + options.IdentityId = ManagedIdentityId(ManagedIdentityIdKind::SystemAssigned, {}); + + auto credential = std::make_shared(options); + auto blobClient = BlobClient(blobUrl, credential); + // @end_snippet + } + { + // @begin_snippet: SystemAssignedManagedIdentityBrief + auto credential = std::make_shared(); + auto blobClient = BlobClient(blobUrl, credential); + // @end_snippet + } +} + int main() { try @@ -31,5 +89,7 @@ int main() return 1; } + ShowDifferentManagedIdentityApproaches(); + return 0; } diff --git a/sdk/keyvault/azure-security-keyvault-secrets/README.md b/sdk/keyvault/azure-security-keyvault-secrets/README.md index faed53552..7dbee5aea 100644 --- a/sdk/keyvault/azure-security-keyvault-secrets/README.md +++ b/sdk/keyvault/azure-security-keyvault-secrets/README.md @@ -80,9 +80,11 @@ For detailed samples please review the samples provided. First step is to create a SecretClient. + ```cpp auto const keyVaultUrl = std::getenv("AZURE_KEYVAULT_URL"); auto credential = std::make_shared(); + // create client SecretClient secretClient(keyVaultUrl, credential); ``` @@ -91,6 +93,7 @@ SecretClient secretClient(keyVaultUrl, credential); We call the secret client to create a secret. + ```cpp std::string secretName("MySampleSecret"); std::string secretValue("my secret value"); @@ -102,34 +105,36 @@ secretClient.SetSecret(secretName, secretValue); We retrieve a secret by name. + ```cpp // get secret KeyVaultSecret secret = secretClient.GetSecret(secretName).Value; std::string valueString = secret.Value.HasValue() ? secret.Value.Value() : "NONE RETURNED"; -std::cout << "Secret is returned with name " << secret.Name << " and value " - << valueString << std::endl; +std::cout << "Secret is returned with name " << secret.Name << " and value " << valueString + << std::endl; ``` ### Update a secret Updating an existing secret + ```cpp // change one of the properties secret.Properties.ContentType = "my content"; // update the secret KeyVaultSecret updatedSecret = secretClient.UpdateSecretProperties(secret.Properties).Value; -std::string updatedValueString = updatedSecret.Value.HasValue() ? updatedSecret.Value.Value() - : "NONE RETURNED"; -std::cout << "Secret's content type is now " << updatedValueString - << std::endl; +std::string updatedValueString + = updatedSecret.Value.HasValue() ? updatedSecret.Value.Value() : "NONE RETURNED"; +std::cout << "Secret's content type is now " << updatedValueString << std::endl; ``` ### Delete a secret Delete an existing secret. + ```cpp // start deleting the secret DeleteSecretOperation operation = secretClient.StartDeleteSecret(secret.Name); @@ -147,6 +152,7 @@ secretClient.PurgeDeletedSecret(secret.Name); Delete and Purge a secret. + ```cpp // start deleting the secret DeleteSecretOperation operation = secretClient.StartDeleteSecret(secret.Name); @@ -164,6 +170,7 @@ secretClient.PurgeDeletedSecret(secret.Name); List all the secrets in keyvault. + ```cpp // get all the versions of a secret for (auto secretsVersion = secretClient.GetPropertiesOfSecretsVersions(secret1.Name); diff --git a/sdk/keyvault/azure-security-keyvault-secrets/samples/sample1-basic-operations/sample1_basic_operations.cpp b/sdk/keyvault/azure-security-keyvault-secrets/samples/sample1-basic-operations/sample1_basic_operations.cpp index dd55a2ba0..e730089bc 100644 --- a/sdk/keyvault/azure-security-keyvault-secrets/samples/sample1-basic-operations/sample1_basic_operations.cpp +++ b/sdk/keyvault/azure-security-keyvault-secrets/samples/sample1-basic-operations/sample1_basic_operations.cpp @@ -21,8 +21,8 @@ using namespace std::chrono_literals; int main() { - auto const keyVaultUrl = std::getenv("AZURE_KEYVAULT_URL"); // @begin_snippet: SecretSample1CreateCredential + auto const keyVaultUrl = std::getenv("AZURE_KEYVAULT_URL"); auto credential = std::make_shared(); // create client