{"id":87283,"date":"2020-05-28T17:26:21","date_gmt":"2020-05-28T17:26:21","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=87283"},"modified":"2021-04-29T15:25:05","modified_gmt":"2021-04-29T15:25:05","slug":"how-to-create-an-ubuntu-powershell-development-environment-part-3","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/how-to-create-an-ubuntu-powershell-development-environment-part-3\/","title":{"rendered":"How to Create an Ubuntu PowerShell Development Environment \u2013 Part 3"},"content":{"rendered":"<p><strong>The series so far:<\/strong><\/p>\n<ol>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/database-devops-sql\/how-to-create-an-ubuntu-powershell-development-environment-part-1\/\">How to Create an Ubuntu PowerShell Development Environment \u2013 Part 1<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/sql-linux\/how-to-create-an-ubuntu-powershell-development-environment-part-2\/\">How to Create an Ubuntu PowerShell Development Environment \u2013 Part 2<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/sql-linux\/how-to-create-an-ubuntu-powershell-development-environment-part-3\/\">How to Create an Ubuntu PowerShell Development Environment \u2013 Part 3<\/a><\/li>\n<\/ol>\n\n<p>Over the last few years, Microsoft has made great strides in making their software products available on a wider range of platforms beyond Windows. Many of their products will now run on a variety of Linux distributions (often referred to as \u201cdistros\u201d), as well as Apple\u2019s macOS platform. This includes their database product, SQL Server.<\/p>\n<p>One way in which Microsoft achieved cross-platform compatibility is through containers. If you aren\u2019t familiar with containers, you can think of them as a stripped-down virtual machine. Only the components necessary to run the application, in this case, SQL Server, are included. The leading tool to manage containers is called Docker. Docker is an application that will allow you to download, create, start and stop, and run containers. If you want a more detailed explanation of containers, please see the article <a href=\"https:\/\/www.docker.com\/resources\/what-container\">What is a Container<\/a> on Docker\u2019s website.<\/p>\n<h2>Assumptions<\/h2>\n<p>For this article, you should understand the concepts of a container, although no experience is required. See the article from Docker referenced in the previous section if you desire more enlightenment on containers. Additionally, this article assumes you are familiar with the SQL language, as well as some basics of PowerShell. Note that throughout this article, when referencing PowerShell, it\u2019s referring to the PowerShell Core product.<\/p>\n<h2>The Platform<\/h2>\n<p>The previous articles, How to Create an Ubuntu PowerShell Development Environment <a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/database-devops-sql\/how-to-create-an-ubuntu-powershell-development-environment-part-1\/\">Part 1<\/a> and <a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/sql-linux\/how-to-create-an-ubuntu-powershell-development-environment-part-2\/\">Part 2<\/a>, walked through the steps of creating a virtual machine for Linux development and learning. That VM is the basis for this article. All the code demos in this article were created and run in that specific virtual computer. For best results, you should first follow the steps in that article to create a VM. From there, you will be in a good place to follow along with this article. However, they have been tested on other variations of Ubuntu, CentOS, as well as on macOS.<\/p>\n<p>In those articles, I showed not just the creation of the virtual machine, but the steps to install PowerShell and Visual Studio Code (VSCode), tools you will need in order to complete the demos in this article should you wish to follow along.<\/p>\n<p>For the demo, I am assuming you have download the demo files and opened them in Visual Studio Code within the virtual machine, and are executing individual samples by highlighting the code sample and using the F8 key, or by right-clicking on the selected text and picking run.<\/p>\n<h2>The Demo<\/h2>\n<p>The code samples in this article are part of a bigger sample I provide on my GitHub site. You\u2019ll find the entire project <a href=\"https:\/\/github.com\/arcanecode\/PowerShellCore\">here<\/a>. There is a zip file included that contains everything in one easy download, or you can look through GitHub and pick and choose the files you want. GitHub also displays Markdown correctly, so you may find it easier to view the project documentation via GitHub rather than in VSCode.<\/p>\n<p>This article uses two specific files, located in the Demo folder: <em>m11-cool-things-1-docker.ps1<\/em> and <em>m11-install-docker.sh<\/em>. While this article will extract the relevant pieces and explain them, you will find it helpful to review the entire script in order to understand the overall flow of the code.<\/p>\n<h2>The Beginning<\/h2>\n<p>The first thing the PowerShell script does is use the <code>Set-Location<\/code> cmdlet to set the current location to the folder where you extracted the demo code. This location should have the Demo, Notes, and Extras folders under it.<\/p>\n<p>Next, make sure Docker is installed, and if not, install it. The command to do this is rather interesting.<\/p>\n<pre class=\"lang:none theme:none\">bash .\/Demo\/m11-install-docker.sh<\/pre>\n<p>bash is very similar to PowerShell; it is both a terminal and a scripting language. It is native to many Linux distros, including the Ubuntu-based ones. This code uses PowerShell to start a bash session and then executes the bash script <em>m11-install-docker.sh<\/em>. When the script finishes executing, the bash session ends.<\/p>\n<p>Take a look inside that bash script.<\/p>\n<pre class=\"lang:none theme:none\">if [ -x \"$(command -v docker)\" ]; then\r\n    echo \"Docker is already installed\"\r\nelse\r\n    echo \"Installing Docker\"\r\n    sudo snap install docker\r\nfi<\/pre>\n<p>The first line attempts to run a command that will complete successfully if Docker is installed. If so, it simply displays that information to the screen via the <code>echo<\/code> command.<\/p>\n<p>If Docker is not installed, then the script will attempt to install Docker using the <em>snap<\/em> utility. Snap is a package manager introduced in the Ubuntu line of distros; other distros use a manager known as <em>flat packs<\/em>. On macOS, <em>brew<\/em> is the package manager of choice. This is one part of the demo you may need to alter depending on your distro. See the documentation for your specific Linux install for more details.<\/p>\n<p>Of course, there are other ways to install Docker. The point of these few lines was to demonstrate how easy it is to run bash scripts from inside your PowerShell script.<\/p>\n<h2>Pulling Your Image<\/h2>\n<p>A Docker <em>image<\/em> is like an ISO. Just as you would use an ISO image to create a virtual machine, a Docker image file can be used to generate one or more containers. Docker has a vast library of images, built by itself and by many companies, such as Microsoft. These images are available to download and use in <code>your<\/code> own environments.<\/p>\n<p>For this demo, you are going to pull the image for SQL Server 2017 using the following command.<\/p>\n<pre class=\"lang:none theme:none\">sudo docker pull mcr.microsoft.com\/mssql\/server:2017-latest<\/pre>\n<p>The <code>sudo<\/code> command executes the following <code>docker<\/code> program with administrative privileges. Docker, as stated earlier, is the application which manages the containers. Then you give the instruction to Docker, <code>pull<\/code>. Pull is the directive to download a container from Docker\u2019s repositories.<\/p>\n<p>The final piece is the image to pull. The first part, <code>mcr.microsoft.com<\/code>, indicates this image is stored in the Microsoft area of the Docker repositories. As you might guess, <code>mssql<\/code> indicates the subfolders containing SQL Server images, and <code>server:2017-latest<\/code> indicates the version of SQL Server to pull, 2017. The <code>-latest<\/code> indicates this should be the most currently patched version; however, it is possible to specify a specific version.<\/p>\n<p>Once downloaded, it is a good idea to query your local image cache to ensure the download was successful. You can do so using this simple command.<\/p>\n<pre class=\"lang:none theme:none\">sudo docker image ls<\/pre>\n<p><code>image<\/code> tells Docker you want to work with images, and <code>ls<\/code> is a simple listing command, similar to using <code>ls<\/code> to list files in the bash shell.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"813\" height=\"49\" class=\"wp-image-87284\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/05\/word-image-36.png\" \/><\/p>\n<h2>Running the Container<\/h2>\n<p>Now that the image is in place, you need to create a container to run the SQL Server. Unlike traditional SQL Server configuration, this turns out to be quite simple. The following command is used to not only create the container but run it. Note the backslash at the end of each line is the line continuation character for bash, the interpreter that will run this command (even though you\u2019re in PowerShell). You could also choose to remove the backslashes and just type the command all on one line.<\/p>\n<pre class=\"lang:none theme:none\">sudo docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=passW0rd!' -p 1433:1433 --name arcanesql -d mcr.microsoft.com\/mssql\/server:2017-latest<\/pre>\n<p>The first part of the line starts by passing the <code>run<\/code> command into Docker, telling it to create and run a new container. In the first <code>-e<\/code> parameter you are accepting the end user license agreement. In the second <code>-e<\/code> parameter, you create the SA (system administrator) password. As you can see, I\u2019ve used a rather simple password, you should definitely use something much more secure.<\/p>\n<p>Next, we need to map a port number for the container using the <code>-p<\/code> parameter. The first port number will be used to listen on the local computer, the second port number is used in the container. SQL Server listens on port 1433 by default, so we\u2019ll use that for both parts of the mapping.<\/p>\n<p>The next parameter, <code>--name<\/code>, provides the name for the container; here I\u2019m calling it <code>arcanesql<\/code>.<\/p>\n<p>In the final parameter, <code>-d<\/code>, you need to indicate what image file should be used to generate the container. As you can see, the command is using the SQL Server image downloaded in the previous step.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1195\" height=\"77\" class=\"wp-image-87285\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/05\/word-image-37.png\" \/><\/p>\n<p>You can verify the container is indeed running using the following command.<\/p>\n<pre class=\"lang:none theme:none\">sudo docker container ls<\/pre>\n<p>As with the other commands, the third parameter indicates what type of Docker object to work with, here containers. Like with image, the <code>ls<\/code> will produce a list of running containers.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1189\" height=\"87\" class=\"wp-image-87286\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/05\/word-image-38.png\" \/><\/p>\n<h2>Installing the SQL Server Module<\/h2>\n<p>Now that SQL Server is up and running, it\u2019s time to start interacting with it from PowerShell Core. First, though, install the PowerShell Core SQL Server module.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Install-Module SqlServer<\/pre>\n<p>It won\u2019t hurt to run this if the SQL Server module is already installed. If it is PowerShell will simply provide a warning message to that effect.<\/p>\n<p>If you\u2019ve already installed it, and simply want to make sure it is up to date, you can use the cmdlet to update an already installed module.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Update-Module SqlServer<\/pre>\n<p>Do note that normally you would not want to include these in every script you write. You would just need to ensure the computer you are running on has the SQL Server module installed, and that you update it on a regular basis, testing your scripts of course after an update. (For more about testing PowerShell code, see my three-part article on Pester, the PowerShell testing framework, beginning with <a href=\"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/powershell\/introduction-to-testing-your-powershell-code-with-pester\/\">Introduction to Testing Your PowerShell Code with Pester<\/a> here on SimpleTalk.)<\/p>\n<h2>Running Your First Query<\/h2>\n<p>The first query will be very simple; it will return a listing of all tables in the master database so you can see how easy it is to interact with SQL Server and learn the basic set of parameters.<\/p>\n<p>The basic cmdlet to work with SQL Server is <code>Invoke-SqlCmd<\/code>. It requires a set of parameters, so you\u2019ll place those in variables for easy reference.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$serverName = 'localhost,1433'\r\n$dbName = 'master'\r\n$userName = 'sa'\r\n$pw = 'passW0rd!'\r\n$queryTimeout = 50000\r\n$sql = 'SELECT * FROM master.INFORMATION_SCHEMA.Tables'<\/pre>\n<p>For this exercise, you are running the Docker container on the same computer as your PowerShell session, so you can just use <em>localhost<\/em> for the server name. Obviously, you\u2019ll replace this with the name of your server when working in other environments. Note that you must append the port number after the server name.<\/p>\n<p>Next, you have the database name you\u2019ll be working with, and for this example, it will be <em>master<\/em>.<\/p>\n<p>The next two parameters are the username and password. In a real-world environment, you\u2019d be setting up real usernames and passwords, but this demo will be simple and just use the SA (system administrator) account built into SQL Server. The password is the same one used when you created and ran the container using the <code>docker run<\/code> command.<\/p>\n<p>Next up is the query timeout. How long do should PowerShell wait before realizing no one is answering and give up? The timeout is measured in terms of seconds.<\/p>\n<p>The last parameter is the query to run. Here you are running a simple <code>SELECT<\/code> statement to list the tables in the master database.<\/p>\n<p>Now that the parameters are established in variables, you are ready to call the <code>Invoke-SqlCmd<\/code> cmdlet to run the query.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Invoke-Sqlcmd -Query $sql `\r\n              -ServerInstance $serverName `\r\n              -Database $dbName `\r\n              -Username $userName `\r\n              -Password $pw `\r\n              -QueryTimeout $queryTimeout<\/pre>\n<p>Here you pass in the variables to each named parameter. Note the backtick symbol at the end of each line except the last. This is the line continuation character; it allows you to spread out lengthy commands across multiple lines to make them easier to read.<\/p>\n<p>In the output, you see a list of each table in the master database.<\/p>\n<p><img decoding=\"async\" class=\"wp-image-87287\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/05\/word-image.tiff\" \/><\/p>\n<h2>Splatting<\/h2>\n<p>As you can see, <code>Invoke-SqlCmd<\/code> has a fairly lengthy parameter set. It will get tiresome to have to repeat this over and over each time you call <code>Invoke-SqlCmd<\/code>, especially as the bulk of these will not change between calls.<\/p>\n<p>To handle this, PowerShell includes a technique called <em>splatting<\/em>. With splatting, you create a hash table, using the names of the parameters for the hash table keys, and the values for each parameter as the hash table values.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$sqlParams = @{ \"ServerInstance\" = $serverName\r\n                \"Database\" = $dbName\r\n                \"Username\" = $userName\r\n                \"Password\" = $pw\r\n                \"QueryTimeout\" = $queryTimeout\r\n              }<\/pre>\n<p>If you look at the syntax in the previous code example, you\u2019ll see that the key values on the left of the hash table above match the parameter names. For this example, load the values from the variables you created, but you could also have hardcoded the values.<\/p>\n<p>So how do you use splatting when calling a cmdlet? Well, that\u2019s pretty simple. In this next example, you\u2019ll load the <code>$sql<\/code> variable with a query to create a new database named <em>TeenyTinyDB<\/em>, and then execute the <code>Invoke-SqlCmd<\/code>.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$sql = 'CREATE DATABASE TeenyTinyDB'\r\nInvoke-Sqlcmd -Query $sql @sqlParams<\/pre>\n<p>Here you call <code>Invoke-SqlCmd<\/code>, then pass in the query as a named parameter. After that, you pass in the hash table variable <code>sqlParams<\/code>, but with an important distinction. To make splatting work, you use the <code>@<\/code> symbol instead of the normal <code>$<\/code> for a variable. When PowerShell sees the <code>@ <\/code>symbol, it knows to deconstruct the hash table and use the key\/values as named parameters and their corresponding values.<\/p>\n<p>There are two things to note. I could have included the <code>$sql<\/code> as another value in the hash table. It would have looked like <code>\u201cQuery\u201d = $sql<\/code> (or the actual query as a hard-coded value). In the demo, I made them separate to demonstrate that it is possible to mix named parameters with splatting. On a personal note, I also think it makes the code cleaner if the values that change on each call are passed as named parameters and the values that remain fairly static to become part of the splat.<\/p>\n<p>Second, the technique of splatting applies to all cmdlets in PowerShell, not just <code>Invoke-SqlCmd<\/code>. Feel free to implement this technique in your own projects.<\/p>\n<p>When you execute the command, you don\u2019t get anything in return. On the SQL Server, the new database was created, but because you didn\u2019t request anything back, PowerShell simply returns to the command line.<\/p>\n<h2>Creating Tables<\/h2>\n<p>For the next task, create a table to store the names and URLs of some favorite YouTube channels. Because you\u2019ll be working with the new <em>TeenyTinyDB<\/em> instead of <em>master<\/em>, you will need to update the Database key\/value pair in the hash table.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$dbName = 'TeenyTinyDB'\r\n$sqlParams[\"Database\"] = $dbName<\/pre>\n<p>Technically I could have assigned the database name without the need for the <code>$dbName<\/code> variable. However, I often find myself using these values in other places, such as an informational message. Perhaps a <code>Write-Debug \u201cPopulating $dbName\u201d<\/code> message in my code. Placing items like the database name in a variable makes these tasks easy.<\/p>\n<p>With the database value updated, you can now craft a SQL statement to create a table then execute the command by once again using <code>Invoke-SqlCmd<\/code>.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$sql = @'\r\nCREATE TABLE [dbo].[FavoriteYouTubers]\r\n(\r\n    [FYTID]       INT            NOT NULL PRIMARY KEY\r\n  , [YouTubeName] NVARCHAR(200)  NOT NULL\r\n  , [YouTubeURL]  NVARCHAR(1000) NOT NULL\r\n)\r\n'@\r\nInvoke-Sqlcmd -Query $sql @sqlParams<\/pre>\n<p>In this script, you take advantage of PowerShell\u2019s <em>here string<\/em> capability to spread the create statement over multiple lines. If you are not familiar with here strings, it is the ability to assign a multi-line string to a variable. To start a here string, you declare the variable then make <code>@<\/code> followed by a quotation mark, either single quote or double quote, the last thing on the line. Do note it has to be last; you cannot have anything after it such as a comment.<\/p>\n<p>The next one or more lines are what you want the variable to contain. As you can see, here strings make it easy to paste in SQL statements of all types.<\/p>\n<p>To close out a here string, simply put the closing quotation mark followed by the <code>@<\/code> sign in the first two positions of a line. This <em>has<\/em> to be in the first two characters if you attempt to indent the here string won\u2019t work.<\/p>\n<p>With the here string setup, call <code>Invoke-SqlCmd<\/code> to create the table. As with the previous statement, it doesn\u2019t produce any output, and it simply returns us to the command line.<\/p>\n<h2>Loading Data<\/h2>\n<p>In this example, create a variable with a SQL query to load multiple rows via an <code>INSERT <\/code>statement and execute it.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$sql = @'\r\nINSERT INTO [dbo].[FavoriteYouTubers]\r\n  ([FYTID], [YouTubeName], [YouTubeURL])\r\nVALUES\r\n  (1, 'AnnaKatMeow', 'https:\/\/www.youtube.com\/channel\/UCmErtDPkJe3cjPPhOw6wPGw')\r\n, (2, 'AdultsOnlyMinecraft', 'https:\/\/www.youtube.com\/user\/AdultsOnlyMinecraft')\r\n, (3, 'Arcane Training and Consulting', 'https:\/\/www.youtube.com\/channel\/UCTH58i-Gl1bZeATOeC4f25g')\r\n, (4, 'Arcane Tube', 'https:\/\/www.youtube.com\/channel\/UCkR0kwYjQ_gngZ8jE3ki7xw')\r\n, (5, 'PowerShell Virtual Chapter', 'https:\/\/www.youtube.com\/channel\/UCFX97evt_7Akx_R9ovfiSwQ')\r\n'@\r\nInvoke-Sqlcmd -Query $sql @sqlParams<\/pre>\n<p>For simplicity, I\u2019ve used a single statement. There are, in fact, many options you could employ. Reading data from a file in a foreach loop and inserting rows as needed, for example.<\/p>\n<p>Like the previous statements, nothing is returned after the query executes, and you are returned to the command prompt.<\/p>\n<h2>Reading Data<\/h2>\n<p>People are funny. They love putting their data into databases. But then they actually expect to get it back! Pesky humans.<\/p>\n<p>Fortunately, PowerShell makes it easy to return data from SQL Server. Follow the same pattern as before&#8211;set up a query and store it in a variable, then use <code>Invoke-SqlCmd<\/code> to execute it.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$sql = @'\r\nSELECT [FYTID]\r\n     , [YouTubeName]\r\n     , [YouTubeURL] \r\n  FROM dbo.FavoriteYouTubers\r\n'@\r\nInvoke-Sqlcmd -Query $sql @sqlParams<\/pre>\n<p>Unlike the previous queries, this actually generates output.<\/p>\n<p><img decoding=\"async\" class=\"wp-image-87288\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/05\/word-image-1.tiff\" \/><\/p>\n<p>Here you can see each row of data, and the values for each column. I want to be very precise about what PowerShell returns.<\/p>\n<p>This is a <em>collection<\/em> of <em>data row objects<\/em>. Each data row has properties and methods. The <code>sqlserver<\/code> module converts each column into a property of the data row object.<\/p>\n<p>The majority of the time, you will want to work with the data returned to PowerShell, not just display it to the screen. To do so, first assign the output of <code>Invoke-SqlCmd<\/code> to a variable.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$data = Invoke-Sqlcmd -Query $sql @sqlParams<\/pre>\n<p>If you want to see the contents of the variable, simply run just the variable name.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$data<\/pre>\n<p>This will display the contents of the collection variable <code>$data<\/code>, displaying each row object, and the properties for each row.<\/p>\n<p><img decoding=\"async\" class=\"wp-image-87289\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/05\/word-image-2.tiff\" \/><\/p>\n<p>You can also iterate over the <code>$data<\/code> collection, here\u2019s a simple example.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">foreach($rowObject in $data)\r\n{\r\n  \"$($rowObject.YouTubeName) is a favorite YouTuber!\"\r\n}<\/pre>\n<p>This sample produces the following output:<\/p>\n<p><img decoding=\"async\" class=\"wp-image-87290\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/05\/word-image-3.tiff\" \/><\/p>\n<p>In this code, I just display a formatted text string, but you could do anything you want to with it, such as writing to an output file.<\/p>\n<h2>Cleanup<\/h2>\n<p>When I was a kid, mom always taught me to put my toys away. There are many reasons why you would want to remove containers you are no longer using. Testing is one, and you may wish to write a script to spin up a new container, load it with data, then let the testers do their thing. When done, you may wish to stop the container or delete it altogether.<\/p>\n<h3>Stopping and Starting Containers<\/h3>\n<p>For a first step, use Docker to see what containers are currently running.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">sudo docker container ls<\/pre>\n<p>The output shows that on the system only one container is running.<\/p>\n<p><img decoding=\"async\" class=\"wp-image-87291\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/05\/word-image-4.tiff\" \/><\/p>\n<p>Take the scenario of wanting to shut down the container, but not removing it. Perhaps you want to turn it off when the testers aren\u2019t using it to save money and resources. To do this, simply use the stop command.<\/p>\n<p>After issuing a stop, you should do another listing to ensure it is, in fact, stopped. You might think you could do another container ls, but note I said it lists <em>currently running<\/em> containers. If you want to see all containers, running or not, you must use a slightly different Docker command.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">sudo docker stop arcanesql \r\nsudo docker ps -a<\/pre>\n<p>The stop command will stop the container with the name passed in. The <code>ps -a<\/code> command will list all containers running or not.<\/p>\n<p><img decoding=\"async\" class=\"wp-image-87292\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/05\/word-image-5.tiff\" \/><\/p>\n<p>If you look at the <code>STATUS<\/code> column, on the very right side of the output, you\u2019ll see the word <code>Exited<\/code>, along with how long in the past it exited. This is the indicator the container is stopped.<\/p>\n<p>In this example, say it is the next morning. The testers are ready to get to work, so start the container back up.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">sudo docker container start arcanesql<\/pre>\n<p>All that is needed is to issue the start command, specifying container, and provide the name of the container to start.<\/p>\n<p><img decoding=\"async\" class=\"wp-image-87293\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/05\/word-image-6.tiff\" \/><\/p>\n<p>As you can see, the status column now begins with <em>Up<\/em> and indicates the length of time this container has been running.<\/p>\n<h3>Deleting a Container<\/h3>\n<p>At some point, you will be done with a container. Perhaps testing is completed, or you want to recreate the container, resetting it for the next round of testing.<\/p>\n<p>Removing a container is even easier than creating it. First, you\u2019ll need to reissue the stop command, then follow it with the Docker command to remove (rm) the named container.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">sudo docker stop arcanesql \r\nsudo docker rm arcanesql<\/pre>\n<p>If you want to be conservative with you keystrokes, you can do this with a single command.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">sudo docker rm --force arcanesql<\/pre>\n<p>The <code>\u2013force<\/code> switch will make Docker stop the container if it is still running, then remove it.<\/p>\n<p>You can verify it is gone by running or Docker listing command.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">sudo docker ps -a<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1556\" height=\"79\" class=\"wp-image-87294\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/05\/word-image-39.png\" \/><\/p>\n<p>As you can see, nothing is returned. Of course, if you had other containers, they would be listed, but the <code>arcanesql<\/code> container would be gone.<\/p>\n<h2>Removing the Image<\/h2>\n<p>Removing the container does not remove the image the container was based on. Keeping the image can be useful for when you are ready to spin up a new container based on the image. Re-run the Docker listing command to see what images are on the system.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">sudo docker image ls<\/pre>\n<p>The output shows the image downloaded earlier in this article.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1556\" height=\"79\" class=\"wp-image-87295\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/05\/word-image-40.png\" \/><\/p>\n<p>As you can see, 1.3 GB is quite a bit of space to take up. In addition, you can see that the image was created 2 months ago. Perhaps a new one has come out, and you want to update to the latest\u2014all valid reasons for removing the image.<\/p>\n<p>To do so, use a similar pattern as the one for the container. You\u2019ll again use rm, but specify it is an image to remove and specify the exact name of the image.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">sudo docker image rm mcr.microsoft.com\/mssql\/server:2017-latest<\/pre>\n<p>When you do so, Docker will show us what it is deleting.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1273\" height=\"336\" class=\"wp-image-87296\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/05\/word-image-41.png\" \/><\/p>\n<p>With that done, you can run another image listing using the image ls command to verify it is gone.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"994\" height=\"83\" class=\"wp-image-87297\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/05\/word-image-42.png\" \/><\/p>\n<p>The image no longer appears. Of course, if you had other images you had downloaded, they would appear here, but the one for the latest version of SQL Server would be absent.<\/p>\n<h2>Conclusion<\/h2>\n<p>In this article, you saw how to use Docker, from within PowerShell Core, to download an image holding SQL Server 2017. You then created a container from that image.<\/p>\n<p>For the next step, you installed the PowerShell SqlServer module, ran some queries to create a table and populate it with data. You then read the data back out of the database so you could work with it. Along the way, you learned the valuable concept of splatting.<\/p>\n<p>Once finishing the work, you learned how to start and stop a container as well as remove it and the image on which it was based.<\/p>\n<p>This article just scratched the service of what you can do when you combine Docker, SQL Server, and PowerShell Core. As you continue to learn, you\u2019ll find even more ways to combine PowerShell Core, SQL Server and Docker.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Running SQL Server in a container may seem daunting at first, but it\u2019s easy once you understand the handful of commands needed. In this article, Robert Cain demonstrates how to run SQL Server in a Docker container on Ubuntu.&hellip;<\/p>\n","protected":false},"author":316962,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143527,143516,53],"tags":[95506],"coauthors":[52865],"class_list":["post-87283","post","type-post","status-publish","format-standard","hentry","category-database-administration-sql-server","category-database-devops","category-featured","tag-automate"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/87283","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/316962"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=87283"}],"version-history":[{"count":4,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/87283\/revisions"}],"predecessor-version":[{"id":87299,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/87283\/revisions\/87299"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=87283"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=87283"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=87283"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=87283"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}