Introduction
I know, another week, another post about a CLI plug-in. I really didn't intend this to be the case, but I recently submitted a (second generation) managed package for security review and had to open up additional IP addresses to allow the security team access to a test org. I'm likely to submit more managed packages for review in the future, so I wanted to find a scriptable way of doing this.
It turned out to be a little more interesting than I'd originally expected, for a couple of reasons.
It Requires the Metadata API
To add an IP range, you have to deploy security settings via the metadata API.
I have done this before, to
enable/disable parallel Apex unit tests, but this was a little different. If I create a security settings file with
the new range(s) and deploy them, per the
Metadata API Docs, all existing IP ranges will be turned off:
To add an IP range, deploy all existing IP ranges, including the one you want to add. Otherwise, the existing IP ranges are replaced with the ones you deploy.
Definitely not what I want!
It Requires a Retrieve and Deploy
In order to get the existing IP addresses, I have to carry out a metadata
retrieve of the security settings from the Salesforce org, add the ranges,
then deploy them. No problem here, I can simply use the retrieve method on the
metadata class that I'm already using to deploy. Weirdly, unlike the deploy
function, the retrieve function doesn't return a promise, instead it expected
me to supply a callback. I couldn't face returning to callback hell after the
heaven of async/await in my plug-in development, so I used the Node Util.Promisify
function that turns it into a function that returns a promise. Very cool.
const asyncRetrieve = promisify(conn.metadata.retrieve); const retrieveCheck = await asyncRetrieve.call(...);
The other interesting aspect is that I get the settings back in XML format,
but I want a JavaScript object to manipulate, which I then need to turn back
into XML to deploy.
To turn XML into JavaScript I use fast-xml-parser, as this has stood me in
good stead with my Org Documentor. To get at the NetworkAccess element:
import { parse} from 'fast-xml-parser'; ... const settings = readFileSync(join(tmpDir, 'settings', 'Security.settings'), 'utf-8'); const md = parse(settings); let networkAccess = md.SecuritySettings.networkAccess;
Once I've added my new ranges, I convert back to XML using xml2js:
import { Builder } from 'xml2js'; ... const builder = new Builder( {renderOpts: {pretty: true, indent: ' ', newline: '\n'}, stringify: { attValue(str) { return str.replace(/&/g, '&') .replace(/"/g, '"') .replace(/'/g, '''); } }, xmldec: { version: '1.0', encoding: 'UTF-8' } }); const xml = builder.buildObject(md);
The Plug-In
This is available as a new command on the bbsfdx plug-in - if you have it
installed already, just run
sfdx plugins: update
to update to version 1.4
If you don't have it installed already (I won't lie, that hurts) you can run:
sfdx plugins:install bbsfdx
and to add one or more ranges:
sfdx bb:iprange:add -r 192.168.2.1,192.168.2.4:192.168.2.255 -u <user>
The -r switch defines the range(s) - comma separate them. For a range, separate the start and end addresses with a colon. For a single address, just skip the colon and the end address.
Hello,
ReplyDeleteI was excited to see this, but unfortunately didn't worked.
PS PATH\gitmevg> sfdx bb:iprange:add -r 0.0.0.0:255.255.255.255 -u gitmevg_so1
Pending sleeping for 5 seconds
ERROR running bb:iprange:add: ENOENT: no such file or directory, mkdir '/tmp/bbsfdx_2021122014333633'