{"id":4291,"date":"2025-03-19T16:10:37","date_gmt":"2025-03-19T20:10:37","guid":{"rendered":"https:\/\/infotechguy.net\/?p=4291"},"modified":"2025-03-19T16:10:37","modified_gmt":"2025-03-19T20:10:37","slug":"f5-script-list-unused-ssl-certificates","status":"publish","type":"post","link":"https:\/\/infotechguy.net\/?p=4291","title":{"rendered":"F5 Script &#8212; List Unused SSL Certificates"},"content":{"rendered":"<p>Quick post, script on F5 using bash and TMSH to show unused certs. Use at your own risk, <strong>take a backup first!<\/strong><\/p>\n<h3>Listing Unused Certificates via TMSH<\/h3>\n<p><em><strong>Unused Certificates For Loop&#8230;<\/strong><\/em><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"bash\">for cert in $(tmsh list sys file ssl-cert | grep \"sys file ssl-cert\" | awk '{print $4}'); do\r\n    if ! tmsh list ltm profile client-ssl | grep -q $cert; then\r\n        echo \"Unused certificate: $cert\"\r\n    fi\r\ndone\r\n\r\n{{output}}\r\nUnused certificate: ca-bundle.crt\r\nUnused certificate: f5-ca-bundle.crt\r\nUnused certificate: f5-irule.crt\r\nUnused certificate: f5_api_com.crt\r\nUnused certificate: test30.infotechguy.dev_2025_112491.crt\r\nUnused certificate: test30.infotechguy.dev_2025_140516.crt\r\nUnused certificate: test30.infotechguy.dev_2025_196380.crt\r\nUnused certificate: test30.infotechguy.dev_2025_827939.crt\r\nUnused certificate: test31.infotechguy.dev_2025_112491.crt\r\nUnused certificate: test31.infotechguy.dev_2025_140516.crt\r\nUnused certificate: test31.infotechguy.dev_2025_196380.crt\r\nUnused certificate: test31.infotechguy.dev_2025_827939.crt\r\nUnused certificate: test32.infotechguy.dev_2025_112491.crt\r\nUnused certificate: test32.infotechguy.dev_2025_140516.crt\r\nUnused certificate: test32.infotechguy.dev_2025_196380.crt\r\nUnused certificate: test32.infotechguy.dev_2025_827939.crt\r\n<\/pre>\n<p><strong><em>Cool, lets take this further<\/em><\/strong>. let&#8217;s put each entry (take certificates loop) into an array so we can manipulate it later, in a deletion loop.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"bash\"># Declare an array to store unused certificates\r\nunused_certs=()\r\n\r\n# List of certificates to exclude (exclude these system certs)\r\nexclude_list=(\"ca-bundle.crt\" \"f5-ca-bundle.crt\" \"f5-irule.crt\" \"f5_api_com.crt\")\r\n\r\n# Loop through each certificate found in BIG-IP\r\nfor cert in $(tmsh list sys file ssl-cert | grep \"sys file ssl-cert\" | awk '{print $4}'); do\r\n    # Skip certificates in the exclude list\r\n    if [[ \" ${exclude_list[@]} \" =~ \" $cert \" ]]; then\r\n        continue\r\n    fi\r\n\r\n    if ! tmsh list ltm profile client-ssl | grep -q $cert; then\r\n        unused_certs+=(\"$cert\")  # Add unused cert to array\r\n    fi\r\ndone\r\n\r\n# Dry run: Print the certificates that would be deleted\r\necho \"Dry Run: The following unused certificates would be deleted:\"\r\nfor cert in \"${unused_certs[@]}\"; do\r\n    echo \"$cert\"\r\ndone\r\n\r\n{{output}}\r\ntest30.infotechguy.dev_2025_112491.crt\r\ntest30.infotechguy.dev_2025_140516.crt\r\ntest30.infotechguy.dev_2025_196380.crt\r\ntest30.infotechguy.dev_2025_827939.crt\r\ntest31.infotechguy.dev_2025_112491.crt\r\ntest31.infotechguy.dev_2025_140516.crt\r\ntest31.infotechguy.dev_2025_196380.crt\r\ntest31.infotechguy.dev_2025_827939.crt\r\ntest32.infotechguy.dev_2025_112491.crt\r\ntest32.infotechguy.dev_2025_140516.crt\r\ntest32.infotechguy.dev_2025_196380.crt\r\ntest32.infotechguy.dev_2025_827939.crt\r\n\r\n<\/pre>\n<p><em><strong>Nice! Now let&#8217;s paste in the delete loop.<\/strong><\/em><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"bash\"># Delete each unused certificate\r\nfor cert in \"${unused_certs[@]}\"; do\r\n    echo \"Deleting unused certificate: $cert\"\r\n    tmsh delete sys file ssl-cert \"$cert\"\r\ndone\r\n\r\n{{output}}\r\nDeleting unused certificate: test30.infotechguy.dev_2025_112491.crt\r\nDeleting unused certificate: test30.infotechguy.dev_2025_140516.crt\r\nDeleting unused certificate: test30.infotechguy.dev_2025_196380.crt\r\nDeleting unused certificate: test30.infotechguy.dev_2025_827939.crt\r\nDeleting unused certificate: test31.infotechguy.dev_2025_112491.crt\r\nDeleting unused certificate: test31.infotechguy.dev_2025_140516.crt\r\nDeleting unused certificate: test31.infotechguy.dev_2025_196380.crt\r\nDeleting unused certificate: test31.infotechguy.dev_2025_827939.crt\r\nDeleting unused certificate: test32.infotechguy.dev_2025_112491.crt\r\nDeleting unused certificate: test32.infotechguy.dev_2025_140516.crt\r\nDeleting unused certificate: test32.infotechguy.dev_2025_196380.crt\r\nDeleting unused certificate: test32.infotechguy.dev_2025_827939.crt\r\n\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h3>Listing Unused Keys via TMSH<\/h3>\n<p><strong><em>List Unused Keys For Loop, ssl-key<\/em><\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"bash\"># Declare an array to store unused SSL keys\r\nunused_keys=()\r\n\r\n# List of keys to exclude\r\nexclude_key_list=(\"f5_api_com.key\")\r\n\r\n# Loop through each SSL key found in BIG-IP\r\nfor key in $(tmsh list sys file ssl-key | grep \"sys file ssl-key\" | awk '{print $4}'); do\r\n    # Skip keys in the exclude list\r\n    if [[ \" ${exclude_key_list[@]} \" =~ \" $key \" ]]; then\r\n        continue\r\n    fi\r\n\r\n    if ! tmsh list ltm profile client-ssl | grep -q $key; then\r\n        unused_keys+=(\"$key\")  # Add unused key to array\r\n    fi\r\ndone\r\n\r\n# Dry run: Print the SSL keys that would be deleted\r\necho \"Dry Run: The following unused SSL keys would be deleted (excluding system keys):\"\r\nfor key in \"${unused_keys[@]}\"; do\r\n    echo \"$key\"\r\ndone\r\n\r\n{{output}}\r\ntest30.infotechguy.dev_2025_112491.key\r\ntest30.infotechguy.dev_2025_140516.key\r\ntest30.infotechguy.dev_2025_196380.key\r\ntest30.infotechguy.dev_2025_827939.key\r\ntest31.infotechguy.dev_2025_112491.key\r\ntest31.infotechguy.dev_2025_140516.key\r\ntest31.infotechguy.dev_2025_196380.key\r\ntest31.infotechguy.dev_2025_827939.key\r\ntest32.infotechguy.dev_2025_112491.key\r\ntest32.infotechguy.dev_2025_140516.key\r\ntest32.infotechguy.dev_2025_196380.key\r\ntest32.infotechguy.dev_2025_827939.key\r\n\r\n<\/pre>\n<p>Looks good, let&#8217;s nuk&#8217;em.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"bash\"># Delete each unused SSL key\r\nfor key in \"${unused_keys[@]}\"; do\r\n    echo \"Deleting unused SSL key: $key\"\r\n    tmsh delete sys file ssl-key \"$key\"\r\ndone\r\n\r\n{{output}}\r\nDeleting unused SSL key: test30.infotechguy.dev_2025_112491.key\r\nDeleting unused SSL key: test30.infotechguy.dev_2025_140516.key\r\nDeleting unused SSL key: test30.infotechguy.dev_2025_196380.key\r\nDeleting unused SSL key: test30.infotechguy.dev_2025_827939.key\r\nDeleting unused SSL key: test31.infotechguy.dev_2025_112491.key\r\nDeleting unused SSL key: test31.infotechguy.dev_2025_140516.key\r\nDeleting unused SSL key: test31.infotechguy.dev_2025_196380.key\r\nDeleting unused SSL key: test31.infotechguy.dev_2025_827939.key\r\nDeleting unused SSL key: test32.infotechguy.dev_2025_112491.key\r\nDeleting unused SSL key: test32.infotechguy.dev_2025_140516.key\r\nDeleting unused SSL key: test32.infotechguy.dev_2025_196380.key\r\nDeleting unused SSL key: test32.infotechguy.dev_2025_827939.key\r\n\r\n\r\n<\/pre>\n<p>Sweet!<\/p>\n<p>Hope this is a quick find for someone in the same circumstance, or until F5 makes it easier to do via the GUI.<\/p>\n<p><em><strong>Tools used:<\/strong><\/em><\/p>\n<ul>\n<li>VS Code Bash<\/li>\n<li>F5 VE v17<\/li>\n<li>OpenAI to help me with my loop logic.<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Quick post, script on F5 using bash and TMSH to show unused certs. Use at your own risk, take a backup first! Listing Unused Certificates via TMSH Unused Certificates For Loop&#8230; for cert in $(tmsh&#46;&#46;&#46;<\/p>\n","protected":false},"author":2,"featured_media":4241,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-4291","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-f5"],"_links":{"self":[{"href":"https:\/\/infotechguy.net\/index.php?rest_route=\/wp\/v2\/posts\/4291","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/infotechguy.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/infotechguy.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/infotechguy.net\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/infotechguy.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4291"}],"version-history":[{"count":14,"href":"https:\/\/infotechguy.net\/index.php?rest_route=\/wp\/v2\/posts\/4291\/revisions"}],"predecessor-version":[{"id":4305,"href":"https:\/\/infotechguy.net\/index.php?rest_route=\/wp\/v2\/posts\/4291\/revisions\/4305"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/infotechguy.net\/index.php?rest_route=\/wp\/v2\/media\/4241"}],"wp:attachment":[{"href":"https:\/\/infotechguy.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4291"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/infotechguy.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4291"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/infotechguy.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4291"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}