# Storing Images in Azure Blob Storage The SpecFlow [Output API](https://docs.specflow.org/projects/specflow/en/latest/outputapi/outputapi.html) allows you to display texts and attachments in your IDE’s test explorer output window and also in SpecFlow+LivingDoc. These images are saved and uploaded from your local machine, therefore only visible to you. If you want these images to be viewable you must host these images in another space, for instance, Azure Blob Storage. We have put together the below guide to demonstrate a use case. Please note this is **not** the only solution and there might be other alternatives. ## Prerequisites - A [storage account](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-create?tabs=azure-portal) needs to created in Azure. - A container needs to be created with ***Public read access for blobs only*** permission. You can read more about this on [Microsoft's website](https://docs.microsoft.com/en-us/azure/storage/blobs/anonymous-read-access-configure?tabs=portal#set-the-public-access-level-for-a-container) ![configure public access container](../_static/images/configure-public-access-container.png) - A Service Connections needs to be created for the Azure subscription in Azure DevOps with the permission ***Storage Blob Data Contributor*** configured for the service principal. You can read more about this on [Microsoft's website](https://docs.microsoft.com/en-us/azure/storage/blobs/assign-azure-role-data-access?tabs=portal#assign-an-azure-role) and [here](https://brettmckenzie.net/2020/03/23/azure-pipelines-copy-files-task-authentication-failed/). ## Example You can download this example repo from our [GitHub page](https://github.com/SpecFlowOSS/SpecFlow-Examples/tree/master/BlobStorageAttachments). Our example project works as per following: - Every test-run saves the images locally to a pre-defined folder e.g. *Screenshots*. - The test code receives the URL template for the attachments from the build using an environment variable - After creating and saving the screenshots locally, the code uses the URL template and the file name and generates the URL for the output API: `````CSharp namespace CalculatorSelenium.Specs.Hooks { [Binding] public sealed class LoggingHooks { private readonly ISpecFlowOutputHelper _specFlowOutputHelper; private readonly BrowserDriver _browserDriver; private string? _attachmentTemplate; public LoggingHooks(ISpecFlowOutputHelper specFlowOutputHelper, BrowserDriver browserDriver) { _specFlowOutputHelper = specFlowOutputHelper; _browserDriver = browserDriver; _attachmentTemplate = Environment.GetEnvironmentVariable("ATTACHMENT_TEMPLATE"); } [AfterStep] public void AfterStep() { var screenshot = _browserDriver.Current.TakeScreenshot(); var imagePath = Path.GetFileNameWithoutExtension(Path.GetTempFileName()) + ".png"; Directory.CreateDirectory("Screenshots"); screenshot.SaveAsFile(Path.Combine("Screenshots", imagePath)); if (!string.IsNullOrEmpty(_attachmentTemplate)) _specFlowOutputHelper.AddAttachment(string.Format(_attachmentTemplate, imagePath)); } } } ````` ### Setup in Azure DevOps - The build contains the AzureCopy task which uploads the images from the Screenshots folder to the blob storage to a folder unique to the build In AzDo. In LivingDoc the absolute URLs can then be opened. The following settings needs to be configured for the Azure Copy task: ![Azure blob settings](../_static/images/azureblobsettings.png) - The URL template needs to set as a variable for the build in the Classis pipeline: ![url variable](../_static/images/url.png) - The URL template should match the container name and the blob prefix: ````URL https://livingdocimages.blob.core.windows.net/build-images/$(Build.BuildId)/{0} ```` ***> Note:*** In YAML the URL template can be passed on to the VSTEST task: ````YAML steps: - task: VSTest@2 displayName: 'VsTest - testAssemblies' inputs: testAssemblyVer2: | **\*Specs*.dll !**\*TestAdapter.dll !**\obj\** searchFolder: '$(System.DefaultWorkingDirectory)\CalculatorSelenium' env: ATTACHMENT_TEMPLATE: 'https://livingdocimages.blob.core.windows.net/build-images/$(Build.BuildId)/{0}' ```` ### Important Notes - The container needs ***Blob access*** so anonymous users can access the files. - The AzureCopy task can create the container but with **private** access. The user needs to access container and set the blob access permission as per prerequisites above. - The container name has some restrictions: only lowercase letters, numbers, and dash -. - The URL template needs to be kept sync with the other parameters.